首页 > 其他分享 >线段树维护区间历史版本和

线段树维护区间历史版本和

时间:2022-11-11 19:55:16浏览次数:76  
标签:线段 son curtag tag lson 版本 sumh 区间 sum

好久没写博客了,主要是这玩意网上有点难搜到,就干脆糊了一个
另外区间加操作的题见 这个博客

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

  1. 区间翻转
  2. 设 \(S(v, l, r)\) 为第 \(v\) 个版本的区间 \([l, r]\) 和,求 \(\sum^i_{v=1} S(v, l, r)\)(认为此次产生了一个和版本 \(i-1\) 完全相同的版本 \(i\))

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

为了让我以后快速理解,先详细说明一下 \(tag[0/1]\) 的定义,考虑父节点 \(x\) 和子节点 \(son\),且 \(son\) 刚刚经过了 pushdown 于是所有状态都已是最新,且接下来的对 \(son\) 的操作都暂存在 \(x\) 的 \(tag\) 里——这里的 \(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\),那么 \(son\) 的 \(sum, sumh, curtag, tag[0/1]\) 怎么更新呢?
考虑 \(sumh\) 要加上几个 \(sum\) 和几个 \(len-sum\);显然 \(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]\) 的更新,本质就是把 \(x\) 的 \(tag\) 序列接到 \(son\) 的 \(tag\) 序列后头,但是需要注意,若 \(curtag[son] = 0\),即 \(son\) 的 \(tag\) 序列尾为 \(0\),那么就直接接上去就行了;否则需要把 \(x\) 的 \(tag\) 序列取反再接上去,即

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

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

curtag[lson] ^= curtag[x]

最后把 \(x\) 的 \(tag\) 序列清空即可

pushup
更新 \(sum\) 和 \(sumh\)

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]]++
当不需要向下递归,即当前区间需要整个翻转时,更新一下 \(x\) 的 sum[x] = len - sum[x] 然后 sumh[x] += sum[x],然后向 \(x\) 的 \(tag\) 序列接一个和原本的尾不同的,即 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;

标签:线段,son,curtag,tag,lson,版本,sumh,区间,sum
From: https://www.cnblogs.com/zhyh/p/16881585.html

相关文章