区间合并是一类问题的统称,种类很多,但在这篇博客中只需实现以下操作即可:
有一个01串,你有三种操作:
前两种操作其实可以算作一个操作,重点在于如何高效地解决第三种操作。
虽说平衡树也可以解决这类问题,但是这里我们使用线段树来解决。
线段树维护四个值(可以缩成三个,使用第四个是为了加强程序的可读性),分别是:
这里博主有点懒,就不画图了,相信听了讲解自己脑补一下也是能搞懂的(听起来真的很简单~~)。
开始假设当前节点为 this t h i s ,其左儿子为 lc l c ,右儿子为 rc r c ,且 lc l c , rc r c 中四个值均准确无误,接下来要对 this t h i s 节点做 pushup() p u s h u p ( ) 操作。
分步骤讨论:
由于本人比较蒟蒻,所以我使用了一个 struct s t r u c t 来存储我需要的 Ans A n s 并逐步更新, Ans A n s 里面存储三个值, ls l s , rs r s 和 s s ,意义应该很明白,和上面的 lsum l s u m , rsum r s u m , sum s u m 一一对应,答案的转移与 pushup() p u s h u p ( ) 相类似由于上面的 pushup() p u s h u p ( ) 使用到了 color c o l o r 来简化操作,现在的 Ans A n s 中不便维护 color c o l o r ,所以不能偷懒了~。
struct SegTree {
#define lc(o) o << 1 //简化操作
#define rc(o) o << 1 | 1
#define mid ((l + r) >> 1)
struct Ans {
int ls, rs, s;
Ans(int ls, int rs, int s) : ls(ls), rs(rs), s(s) {}
};
static const int MAXSIZE = 100000 + 5;
int lsum[MAXSIZE << 2], rsum[MAXSIZE << 2], sum[MAXSIZE << 2], color[MAXSIZE << 2];
void creat(int o, int l, int r, int value) { //更新一个结点
color[o] = value;
lsum[o] = rsum[o] = sum[o] = value ? r - l + 1 : 0; //三目运算符秀了一波~
}
void pushdown(int o, int l, int r) {
if (color[o] != -1) { //如果有color那么pushdown,注意color不会在向子结点更新后发生改变
creat(lc(o), l, mid, color[o]);
creat(rc(o), mid + 1, r, color[o]);
}
}
void pushup(int o, int l, int r) { //繁琐的pushup(),具体已经解释过了
lsum[o] = color[lc(o)] == 1 ? lsum[lc(o)] + lsum[rc(o)] : lsum[lc(o)];
rsum[o] = color[rc(o)] == 1 ? rsum[lc(o)] + rsum[rc(o)] : rsum[rc(o)];
sum[o] = max(rsum[lc(o)] + lsum[rc(o)], max(sum[lc(o)], sum[rc(o)]));
if (sum[o] == 0) color[o] = 0; else if (sum[o] == r - l + 1) color[o] = 1; else color[o] = -1;
}
void set(int o, int l, int r, int L, int R, int value) { //和普通线段树一样的操作
if (l > R || r < L) return;
else if (L <= l && r <= R) creat(o, l, r, value);
else {
pushdown(o, l, r);
set(lc(o), l, mid, L, R, value);
set(rc(o), mid + 1, r, L, R, value);
pushup(o, l, r);
}
}
Ans query(int o, int l, int r, int L, int R) {
if (l > R || r < L) return Ans(0, 0, 0);
else if (L <= l && r <= R) return Ans(lsum[o], rsum[o], sum[o]);
else {
pushdown(o, l, r);
if (R <= mid) return query(lc(o), l, mid, L, R);
if (L > mid) return query(rc(o), mid + 1, r, L, R);
Ans p = query(lc(o), l, mid, L, R);
Ans q = query(rc(o), mid + 1, r, L, R);
return Ans(p.ls == mid - l + 1 ? p.ls + q.ls : p.ls,
q.rs == r - mid ? p.rs + q.rs : q.rs,
max(max(p.s, q.s), p.rs + q.ls));
}
}
void build(int o, int l, int r) {
if (l > r) return;
else if (l == r) creat(o, l, r, a[l]);
else {
build(lc(o), l, mid);
build(rc(o), mid + 1, r);
pushup(o, l, r);
}
}
};