【笔记】线段树维护区间历史版本和
区间加操作的题见 这个博客
给定长为 的01序列, 个询问,第 次认为产生一个新版本 ;要求支持:
- 区间翻转(其实是01反转的反转,下面全写成翻转了,就当我是个丈育。。)
- 设 为第 个版本的区间 和,求 (认为此次产生了一个和版本 完全相同的版本 )
以下是瞎想的做法,应该有更好理解的做法或者直接用矩阵刻画
对每个节点,维护:
当前区间和 ,历史区间和 ,当前待下放的代表是否需要翻转的 ,当前待下放的操作序列(tag 的本质)中,相对原本没有/有翻转的次数
为了让我以后快速理解,先详细说明一下 的定义,考虑父节点 和子节点 ,且 刚刚经过了 pushdown 于是所有状态都已是最新,且接下来的对 的操作都暂存在 的 里——这里的 可以想成 序列(而上面说的 都是这个序列的某个信息),每次向序列尾加入一个 ,表示经过这个操作后,得到的 和原本(刚刚经过 pushdown 后)相比,是/否有翻转。
比如先加了一个翻转操作,那么
然后加了一个不操作的操作,那么
然后又加了一个翻转操作,那么
然后又加了一个翻转操作,那么
此时,回到原本的变量定义,那么当前是否翻转就对应序列 尾,故 ;这个序列里相对原本是/否有翻转的次数 ,就有
pushdown
如果这时候需要下放 ,那么 的 怎么更新呢?
考虑 要加上几个 和几个 ;显然 值还是上次 pushdown 后的状态,也就是 序列中为对应 时的状态,那么有
sumh[son] += sum[son] * tag[x][0] + (len-sum[son]) * tag[x][1]
然后把 更新到最新
if (curtag[x]) sum[son] = len - sum[son]
的更新,本质就是把 的 序列接到 的 序列后头,但是需要注意,若 ,即 的 序列尾为 ,那么就直接接上去就行了;否则需要把 的 序列取反再接上去,即
tag[son][0] += tag[x][0^curtag[son]], tag[son][1] += tag[x][1^curtag[son]]
至于 ,原理同上,和 异或一下就行了
curtag[lson] ^= curtag[x]
最后把 的 序列清空即可
pushup
更新 和
inline void pushup(int x) {
sum[x] = sum[lson] + sum[rson];
sumh[x] = sumh[lson] + sumh[rson];
}
reverse
对每个节点
当需要向下递归时,先 pushdown,将之前的信息下放;然后对于一个儿子,如果需要往下走,那就走;否则,意味着操作区间不和这个儿子有交,那就是对这个儿子执行“不操作”的操作,那就是更新一下这个儿子的历史和 sumh[son] += sum[son]
,然后把儿子的 序列尾复制一个接上去,即 tag[son][curtag[son]]++
当不需要向下递归,即当前区间需要整个翻转时,更新一下 的 sum[x] = len - sum[x]
然后 sumh[x] += sum[x]
,然后向 的 序列接一个和原本的尾不同的,即 curtag[x] ^= 1
然后 tag[x][curtag[x]]++
query
正常 pushdown 并返回 即可
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;
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)