[BZOJ4785][ZJOI2017]树状数组(树套树)

来源:互联网 发布:济南java招聘微信群 编辑:程序博客网 时间:2024/06/11 14:46

可以发现,这个树状数组实际上求的是后缀和。而这样子求出的区间和,只有l1r两个点的差别。于是第二问就转化成A[l1]=A[r]的概率,但是要特判l=1的情况(l=1时正确的概率为ri=1A[i]ni=rA[i](mod2)的概率)。
这里设f(i,j)表示A[i]=A[j]的概率,f(0,j)表示ji=1A[i]ni=jA[i](mod2)的概率。
Q:为什么要维护任意两个点相等的概率,而不是维护一个点为1的概率p(i),询问结果为p(l1)p(r)+(1p(l1))(1p(r))呢?
A:这里每个点为1的概率不是独立的,所以结果不一定是p(l1)p(r)+(1p(l1))(1p(r))。举个例子,如果执行了1 1 2,那么A[1]A[2]1的概率都是12,但A[1]=A[2]的概率是0而不是12。原因很简单:因为对于每个1操作,在[l,r]内只能有一个值被修改,所以在上面的例子里,如果A[1]=1,那么A[2]必须为0,反过来也一样。所以这时候A[1]不可能等于A[2]
回到问题,考虑一个修改操作对f的影响。分4种情况考虑:
1、i,j两个端点,一个在[l,r]内,一个在[l,r]外。那么这时候的A[i]=A[j]的真假就有1rl+1的概率被反转。即i[1,l1],j[l,r]i[l,r],j[r+1,n]f(i,j)=f(i,j)rlrl+1+(1f(i,j))1rl+1
2、i,j两个端点都在[l,r]内。则此时A[i]=A[j]的真假被反转的条件是i被反转或j被反转,即有2rl+1的概率被反转。也就是说,i[l,r],j[l,r]f(i,j)=f(i,j)rl1rl+1+(1f(i,j))2rl+1
3、i=0j[l,r]外。那么被反转的这个点,要么在j的前面(j>r)要么在j的后面(j<l),也就是说jk=1A[k]nk=jA[k](mod2)的真假一定被反转。即j[l,r]f(0,j)=1f(0,j)
4、i=0j[l,r]内。那么可以看出,jk=1A[k]nk=jA[k](mod2)的真假不被反转的充分必要条件是被反转的点恰好为j,即jk=1A[k]nk=jA[k](mod2)的值有rlrl+1的概率被反转。也就是说j[l,r]f(0,j)=f(0,j)1rl+1+(1f(0,j))rlrl+1
而询问就是询问f(l1,r)的值。
可以用线段树套线段树维护f的值,支持二维区间修改和单点询问。实现见代码。
代码:

#include <cmath>#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#define p2 p << 1#define p3 p << 1 | 1using namespace std;inline int read() {    int res = 0; bool bo = 0; char c;    while (((c = getchar()) < '0' || c > '9') && c != '-');    if (c == '-') bo = 1; else res = c - 48;    while ((c = getchar()) >= '0' && c <= '9')        res = (res << 3) + (res << 1) + (c - 48);    return bo ? ~res + 1 : res;}const int N = 4e5 + 193, W = 4e7 + 97, PYZ = 998244353;int n, m, rt[N], QAQ;struct cyx {    int lc, rc, val;    void init() {lc = rc = 0; val = 1;}} T[W];int qpow(int a, int b) {    int res = 1;    while (b) {        if (b & 1) res = 1ll * res * a % PYZ;        a = 1ll * a * a % PYZ;        b >>= 1;    }    return res;}int calc(int x, int y) {    int res = 1ll * x * y % PYZ, u = 1 - x, v = 1 - y;    if (u < 0) u += PYZ; if (v < 0) v += PYZ;    return (res + 1ll * u * v % PYZ) % PYZ;}void modify(int l, int r, int s, int e, int v, int &p) {    if (!p) T[p = ++QAQ].init();    if (l == s && r == e) return (void) (T[p].val = calc(T[p].val, v));    int mid = l + r >> 1;    if (e <= mid) modify(l, mid, s, e, v, T[p].lc);    else if (s >= mid + 1) modify(mid + 1, r, s, e, v, T[p].rc);    else modify(l, mid, s, mid, v, T[p].lc),        modify(mid + 1, r, mid + 1, e, v, T[p].rc);}void change(int l, int r, int s, int e, int st, int ed, int v, int p) {    if (l == s && r == e) return modify(1, n, st, ed, v, rt[p]);    int mid = l + r >> 1;    if (e <= mid) change(l, mid, s, e, st, ed, v, p2);    else if (s >= mid + 1) change(mid + 1, r, s, e, st, ed, v, p3);    else change(l, mid, s, mid, st, ed, v, p2),        change(mid + 1, r, mid + 1, e, st, ed, v, p3);}int query(int l, int r, int x, int p) {    if (!p) return 1; int res = T[p].val;    if (l == r) return res; int mid = l + r >> 1;    if (x <= mid) res = calc(res, query(l, mid, x, T[p].lc));    else res = calc(res, query(mid + 1, r, x, T[p].rc));    return res;}int ask(int l, int r, int x, int y, int p) {    int res = query(1, n, y, rt[p]);    if (l == r) return res; int mid = l + r >> 1;    if (x <= mid) res = calc(res, ask(l, mid, x, y, p2));    else res = calc(res, ask(mid + 1, r, x, y, p3));    return res;}int main() {    int i, op, x, y; n = read(); m = read();    while (m--) {        op = read(); x = read(); y = read();        if (op == 1) {            int v = qpow(y - x + 1, PYZ - 2), w, u = (1 - v + PYZ) % PYZ;            if (x > 1) change(0, n, 1, x - 1, x, y, u, 1);            if (y < n) change(0, n, x, y, y + 1, n, u, 1);            w = (1 - (v << 1) % PYZ + PYZ) % PYZ;            change(0, n, x, y, x, y, w, 1);            if (x > 1) change(0, n, 0, 0, 1, x - 1, 0, 1);            if (y < n) change(0, n, 0, 0, y + 1, n, 0, 1);            change(0, n, 0, 0, x, y, v, 1);        }        else printf("%d\n", ask(0, n, x - 1, y, 1));    }    return 0;}
原创粉丝点击