【笔记】线段树维护区间历史版本和

区间加操作的题见 这个博客

ec-2020 G

给定长为 n 的01序列,m 个询问,第 i 次认为产生一个新版本 i;要求支持:

  1. 区间翻转(其实是01反转的反转,下面全写成翻转了,就当我是个丈育。。)
  2. S(v,l,r) 为第 v 个版本的区间 [l,r] 和,求 v=1iS(v,l,r)(认为此次产生了一个和版本 i1 完全相同的版本 i

以下是瞎想的做法,应该有更好理解的做法或者直接用矩阵刻画

对每个节点,维护:
当前区间和 sum,历史区间和 sumh,当前待下放的代表是否需要翻转的 curtag=1/0,当前待下放的操作序列(tag 的本质)中,相对原本没有/有翻转的次数 tag[0/1]

为了让我以后快速理解,先详细说明一下 tag[0/1] 的定义,考虑父节点 x 和子节点 son,且 son 刚刚经过了 pushdown 于是所有状态都已是最新,且接下来的对 son 的操作都暂存在 xtag 里——这里的 tag 可以想成 01 序列(而上面说的 curtag,tag[0/1] 都是这个序列的某个信息),每次向序列尾加入一个 1/0,表示经过这个操作后,得到的 son 和原本(刚刚经过 pushdown 后)相比,是/否有翻转。
比如先加了一个翻转操作,那么 tag=1
然后加了一个不操作的操作,那么 tag=11
然后又加了一个翻转操作,那么 tag=110
然后又加了一个翻转操作,那么 tag=1101
此时,回到原本的变量定义,那么当前是否翻转就对应序列 1101 尾,故 curtag=1;这个序列里相对原本是/否有翻转的次数 tag[1/0],就有 tag[1]=3,tag[0]=1

pushdown
如果这时候需要下放 tag,那么 sonsum,sumh,curtag,tag[0/1] 怎么更新呢?
考虑 sumh 要加上几个 sum 和几个 lensum;显然 sum 值还是上次 pushdown 后的状态,也就是 tag 序列中为对应 0 时的状态,那么有

sumh[son] += sum[son] * tag[x][0] + (len-sum[son]) * tag[x][1]

然后把 sum 更新到最新

if (curtag[x]) sum[son] = len - sum[son]

tag[son][0/1] 的更新,本质就是把 xtag 序列接到 sontag 序列后头,但是需要注意,若 curtag[son]=0,即 sontag 序列尾为 0,那么就直接接上去就行了;否则需要把 xtag 序列取反再接上去,即

tag[son][0] += tag[x][0^curtag[son]], tag[son][1] += tag[x][1^curtag[son]]

至于 curtag[son],原理同上,和 curtag[x] 异或一下就行了

curtag[lson] ^= curtag[x]

最后把 xtag 序列清空即可

pushup
更新 sumsumh

inline void pushup(int x) {
    sum[x] = sum[lson] + sum[rson];
    sumh[x] = sumh[lson] + sumh[rson];
}

reverse
对每个节点
当需要向下递归时,先 pushdown,将之前的信息下放;然后对于一个儿子,如果需要往下走,那就走;否则,意味着操作区间不和这个儿子有交,那就是对这个儿子执行“不操作”的操作,那就是更新一下这个儿子的历史和 sumh[son] += sum[son],然后把儿子的 tag 序列尾复制一个接上去,即 tag[son][curtag[son]]++
当不需要向下递归,即当前区间需要整个翻转时,更新一下 xsum[x] = len - sum[x] 然后 sumh[x] += sum[x],然后向 xtag 序列接一个和原本的尾不同的,即 curtag[x] ^= 1 然后 tag[x][curtag[x]]++

query
正常 pushdown 并返回 sumh 即可

struct seg {
    #define lson (x<<1)
    #define rson (x<<1|1)
    #define mid ((l+r)>>1)
    int sum[MAXN<<2], tag[MAXN<<2][2], curtag[MAXN<<2];
    ll sumh[MAXN<<2];
    seg() {
        mem(sum, 0), mem(tag, 0), mem(curtag, 0), mem(sumh, 0);
    }
    inline void pushup(int x) {
        sum[x] = sum[lson] + sum[rson];
        sumh[x] = sumh[lson] + sumh[rson];
    }
    inline void pushdown(int x, int l, int r) {
        // lson
        sumh[lson] += 1ll * sum[lson] * tag[x][0];
        sumh[lson] += 1ll * (mid-l+1-sum[lson]) * tag[x][1];
        tag[lson][0] += tag[x][0^curtag[lson]], tag[lson][1] += tag[x][1^curtag[lson]];
        if (curtag[x]) sum[lson] = mid-l+1 - sum[lson];
        curtag[lson] ^= curtag[x];
        // rson
        sumh[rson] += 1ll * sum[rson] * tag[x][0];
        sumh[rson] += 1ll * (r-mid-sum[rson]) * tag[x][1];
        tag[rson][0] += tag[x][0^curtag[rson]], tag[rson][1] += tag[x][1^curtag[rson]];
        if (curtag[x]) sum[rson] = r-mid - sum[rson];
        curtag[rson] ^= curtag[x];
        // x
        tag[x][0] = tag[x][1] = curtag[x] = 0;
    }
    void rev(int x, int l, int r, int _l, int _r) {
        if (l>=_l && r<=_r) {
            sum[x] = r-l+1 - sum[x];
            sumh[x] += sum[x];
            curtag[x] ^= 1;
            tag[x][curtag[x]]++;
        } else {
            pushdown(x, l, r);
            if (mid>=_l) rev(lson, l, mid, _l, _r);
            else sumh[lson] += sum[lson], tag[lson][curtag[lson]]++;
            if (mid< _r) rev(rson, mid+1, r, _l, _r);
            else sumh[rson] += sum[rson], tag[rson][curtag[rson]]++;
            pushup(x);
        }
    }
    ll query(int x, int l, int r, int _l, int _r) {
        if (l>=_l && r<=_r) return sumh[x];
        else {
            pushdown(x, l, r);
            ll t = 0;
            if (mid>=_l) t += query(lson, l, mid, _l, _r);
            if (mid< _r) t += query(rson, mid+1, r, _l, _r);
            return t;
        }
    }
    void print(int x=1, int l=1, int r=N) {
        printf("[%d, %d], sum=%d, sumh=%lld, curtag=%d, tag0=%d, tag1=%d\n",
            l, r, sum[x], sumh[x], curtag[x], tag[x][0], tag[x][1]);
        if (l< r) print(lson, l, mid), print(rson, mid+1, r);
    }
} ST;
posted @   zrkc  阅读(1126)  评论(4编辑  收藏  举报
相关博文:
阅读排行:
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
点击右上角即可分享
微信分享提示