• 【loj



    description

    漆黑的晚上,九条可怜躺在床上辗转反侧。难以入眠的她想起了若干年前她的一次悲惨的 OI 比赛经历。那是一道基础的树状数组题。

    给出一个长度为 (n) 的数组 (A),初始值都为 (0),接下来进行 (m) 次操作,操作有两种:

    • (1 x),表示将 (A_x) 变成 ((A_x + 1)mod 2)
    • (2 l r),表示询问 ((sum_{i=l}^rA_i)mod 2)

    尽管那个时候的可怜非常的 simple,但是她还是发现这题可以用树状数组做。当时非常 young 的她写了如下的算法:

    img

    其中 ({ m lowbit}(x)) 表示数字 (x) 最低的非 (0) 二进制位,例如 ({ m lowbit}(5) = 1, { m lowbit}(12) = 4)。进行第一类操作的时候就调用 ({ m Add}(x)),第二类操作的时候答案就是 ({ m Query}(l, r))

    如果你对树状数组比较熟悉,不难发现可怜把树状数组写错了:({ m Add})({ m Find})(x) 变化的方向反了。因此这个程序在最终测试时华丽的爆 0 了。

    然而奇怪的是,在当时,这个程序通过了出题人给出的大样例——这也是可怜没有进行对拍的原因。

    现在,可怜想要算一下,这个程序回答对每一个询问的概率是多少,这样她就可以再次的感受到自己是一个多么非的人了。然而时间已经过去了很多年,即使是可怜也没有办法完全回忆起当时的大样例。幸运的是,她回忆起了大部分内容,唯一遗忘的是每一次第一类操作的 (x) 的值,因此她假定这次操作的 (x) 是在 ([l_i, r_i]) 范围内等概率随机的。

    具体来说,可怜给出了一个长度为 (n) 的数组 (A),初始为 (0),接下来进行了 (m) 次操作:

    • (1 l r),表示在区间 ([l, r]) 中等概率选取一个 (x) 并执行 ({ m Add}(x))
    • (2 l r),表示询问执行 ({ m Query}(l, r)) 得到的结果是正确的概率是多少。

    原题传送门。

    solution

    根据正常的树状数组推导可得:当 (xleq y)({ m Add}(y)) 会对 ({ m Find}(x)) 产生贡献。

    也就是说其实 ({ m Find}(x)) 是后缀和。不过这里有个坑:({ m Find}(0) = 0) 恒成立。

    (l ot = 1) 时,等价于询问 (A_{l-1}= A_r) 成立的概率。
    (l = 1) 时,等价于询问 (A_r = cnt) 成立的概率(其中 (cnt) 是当前的修改次数)。

    虽然到这一步可以直接用树套树维护,不过由于我人傻,所以推了个公式:

    (p_i) 表示第 (i) 次操作使得等式状态变化的概率。则 (prod[p_ix+(1-p_i)]) 的偶数项之和就是答案。

    考虑代入二次单位根,得到答案为 (frac{1+prod(1-2p_i)}{2})。然后就是三维数点问题。时间复杂度 (O(nlog^2 n))

    【求大佬解释这个式子是否有组合意义】

    accepted code

    #include <cstdio>
    #include <vector>
    #include <algorithm>
    using namespace std;
    
    const int MAXN = 100000;
    const int MOD = 998244353;
    
    inline int add(int x, int y) {x += y; return x >= MOD ? x - MOD : x;}
    inline int sub(int x, int y) {x -= y; return x < 0 ? x + MOD : x;}
    inline int mul(int x, int y) {return (int)(1LL * x * y % MOD);}
    
    int inv[MAXN + 5];
    void init() {
    	inv[1] = 1;
    	for(int i=2;i<=MAXN;i++)
    		inv[i] = sub(0, mul(MOD / i, inv[MOD % i]));
    }
    
    struct node{int a, b, c;};
    vector<node>v[3][MAXN + 5];
    
    int n, m;
    inline int lowbit(int x) {return x & (-x);}
    void add(int o, int x, int p, int k) {
    	for(int i=x;i<=n;i+=lowbit(i))
    		v[o][i].push_back((node){p, k, -1});
    }
    void query(int o, int x, int l, int r, int q) {
    	for(int i=x;i;i-=lowbit(i))
    		v[o][i].push_back((node){l, r, q});
    }
    
    int ch[3][2*MAXN + 5], pro[2*MAXN + 5], ncnt;
    void update(int &x, int l, int r, int p, int k) {
    	if( !x ) x = (++ncnt), ch[0][x] = ch[1][x] = 0, pro[x] = 1;
    	pro[x] = mul(pro[x], k);
    	if( l == r ) return ;
    	int mid = (l + r) >> 1;
    	if( p <= mid ) update(ch[0][x], l, mid, p, k);
    	else update(ch[1][x], mid + 1, r, p, k);
    }
    int prod(int x, int l, int r, int ql, int qr) {
    	if( !x || r < ql || qr < l ) return 1;
    	if( ql <= l && r <= qr ) return pro[x];
    	int mid = (l + r) >> 1;
    	return mul(prod(ch[0][x], l, mid, ql, qr), prod(ch[1][x], mid + 1, r, ql, qr));
    }
    
    int ans[MAXN + 5];
    int main() {
    	init(), scanf("%d%d", &n, &m);
    	
    	int qcnt = 0, nw = 0;
    	for(int i=1,op,l,r;i<=m;i++) {
    		scanf("%d%d%d", &op, &l, &r);
    		if( op == 1 ) {
    			int iv = inv[r - l + 1], p = sub(1, mul(2, iv));
    			add(0, l, r, p), add(1, n - r + 1, n - l + 1, p), add(2, l, r, sub(1, mul(4, iv)));
    			nw ^= 1;
    		} else {
    			if( l == 1 )
    				ans[++qcnt] = (nw ? MOD - 1 : 1), query(1, n - r + 1, n - r + 1, n, qcnt);
    			else {
    				l--, ans[++qcnt] = 1;
    				query(0, l, l, r - 1, qcnt), query(1, n - r + 1, n - r + 1, n - l, qcnt);
    				query(2, l, r, n, qcnt);
    			}
    		}
    	}
    	
    	for(int o=0;o<=2;o++)
    		for(int i=1;i<=n;i++) {
    			int rt = 0; ncnt = 0;
    			for(unsigned j=0;j<v[o][i].size();j++) {
    				node p = v[o][i][j];
    				if( p.c == -1 )
    					update(rt, 1, n, p.a, p.b);
    				else ans[p.c] = mul(ans[p.c], prod(rt, 1, n, p.a, p.b));
    			}
    		}
    	
    	for(int i=1;i<=qcnt;i++)
    		printf("%d
    ", mul(add(ans[i], 1), inv[2]));
    }
    

    details

    l = 1 的答案要特判也太坑了吧。不特判直接暴零。

  • 相关阅读:
    P1582 倒水 (二进制)
    P2014 选课 (树形动规)
    多项式前置技能——复数
    P3694 邦邦的大合唱站队 (状压DP)
    P1754 球迷购票问题 (卡特兰数,递推)
    [SCOI2003]字符串折叠 (区间DP)
    [SDOI2008]仪仗队 (欧拉函数)
    4-字符串
    3.输出,输入,基本数据类型
    2.栈,堆,寄存器的理解
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/13126466.html
Copyright © 2020-2023  润新知