link。
对平衡树的懒标记的应用题,其实和线段树也差不多。
如果不考虑取反操作,那维护操作 \(5\) 就需要知道当前区间答案,当前区间前缀和后缀,因为在 push_up 时我们当前区间的答案肯定等于左区间的答案,右区间的答案以及左区间的后缀加上右区间的前缀这三者间的最大值。
但与线段树不同的一点是,平衡树不是像线段树那样一个节点表示一个区间,它的一个节点就是代表数列中的一个数,所以在 push_up 时其实是 \(3\) 个部分在合并:当前节点,左子树,右子树。
所以在更新答案时我们还要判断当前点是否是 \(1\),如果不是,就不能用左区间后缀加右区间前缀,因为他们中间存在一个 \(0\)。
但加入了操作 \(3\) 又该如何处理呢?
其实很简单,因为是 \(0\) 变 \(1\),\(1\) 变 \(0\),所以我们在维护 \(1\) 的答案的同时维护 \(0\) 的答案,取反的时候直接将 \(0\) 和 \(1\) 的信息 swap 就可以了。
注意:由于是维护数列,所以 FHQ 在 split 的时候是按照 \(size\) 分裂而不是按照 \(key\)。
细节有点多。
code
#include <cstdio>
#include <cstdlib>
#include <utility>
#include <algorithm>
#include <ctime>
using namespace std;
const int N = 1e5 + 5;
struct Treap {
int tot, root;
struct node {
int l, r, key, rank, siz, sum[2], lsum[2], rsum[2], ans[2], tag1; //l, r为左右儿子下标,key为具体是0还是1,siz为子树大小,sum[0/1]为0/1个数,lsum[0/1]为前缀0/1个数,ans[0/1]为最长连续0/1个数,tag1为操作1和2的懒标记
bool tag2; //操作3的懒标记
} a[N];
inline int New(int val) {
a[++tot].rank = rand(), a[tot].key = val;
a[tot].siz = 1, a[tot].tag1 = -1, a[tot].tag2 = false;
if (val) {
a[tot].lsum[1] = a[tot].rsum[1] = a[tot].sum[1] = a[tot].ans[1] = 1;
a[tot].lsum[0] = a[tot].rsum[0] = a[tot].sum[0] = a[tot].ans[0] = 0;
} else {
a[tot].lsum[0] = a[tot].rsum[0] = a[tot].sum[0] = a[tot].ans[0] = 1;
a[tot].lsum[1] = a[tot].rsum[1] = a[tot].sum[1] = a[tot].ans[1] = 0;
}
return tot;
}
inline void change(int rt, int opt) {
if (!opt) { //操作1
a[rt].key = 0;
a[rt].sum[0] = a[rt].lsum[0] = a[rt].rsum[0] = a[rt].ans[0] = a[rt].siz;
a[rt].sum[1] = a[rt].lsum[1] = a[rt].rsum[1] = a[rt].ans[1] = 0;
a[rt].tag1 = 0, a[rt].tag2 = false; //当区间覆盖操作进行后,取反操作被覆盖。
} else if (opt == 1) { //操作2
a[rt].key = 1;
a[rt].sum[0] = a[rt].lsum[0] = a[rt].rsum[0] = a[rt].ans[0] = 0;
a[rt].sum[1] = a[rt].lsum[1] = a[rt].rsum[1] = a[rt].ans[1] = a[rt].siz;
a[rt].tag1 = 1, a[rt].tag2 = false;
} else { //操作3
a[rt].key ^= 1;
swap(a[rt].sum[0], a[rt].sum[1]), swap(a[rt].lsum[0], a[rt].lsum[1]), swap(a[rt].rsum[0], a[rt].rsum[1]), swap(a[rt].ans[0], a[rt].ans[1]);
a[rt].tag2 ^= 1; //取反操作优先级更低。
}
}
inline void push_up(int rt) {
a[rt].siz = a[a[rt].l].siz + a[a[rt].r].siz + 1;
if (a[rt].key) { //分当前点是0/1两种情况讨论
a[rt].sum[1] = a[a[rt].l].sum[1] + a[a[rt].r].sum[1] + 1;
a[rt].ans[1] = max(a[a[rt].l].rsum[1] + a[a[rt].r].lsum[1] + 1, max(a[a[rt].l].ans[1], a[a[rt].r].ans[1]));
a[rt].lsum[1] = a[a[rt].l].lsum[1];
if (a[a[rt].l].lsum[1] == a[a[rt].l].siz)
a[rt].lsum[1] += 1 + a[a[rt].r].lsum[1];
a[rt].rsum[1] = a[a[rt].r].rsum[1];
if (a[a[rt].r].rsum[1] == a[a[rt].r].siz)
a[rt].rsum[1] += 1 + a[a[rt].l].rsum[1];
a[rt].sum[0] = a[a[rt].l].sum[0] + a[a[rt].r].sum[0];
a[rt].ans[0] = max(a[a[rt].l].ans[0], a[a[rt].r].ans[0]);
a[rt].lsum[0] = a[a[rt].l].lsum[0];
a[rt].rsum[0] = a[a[rt].r].rsum[0];
} else {
a[rt].sum[0] = a[a[rt].l].sum[0] + a[a[rt].r].sum[0] + 1;
a[rt].ans[0] = max(a[a[rt].l].rsum[0] + a[a[rt].r].lsum[0] + 1, max(a[a[rt].l].ans[0], a[a[rt].r].ans[0]));
a[rt].lsum[0] = a[a[rt].l].lsum[0];
if (a[a[rt].l].lsum[0] == a[a[rt].l].siz)
a[rt].lsum[0] += 1 + a[a[rt].r].lsum[0];
a[rt].rsum[0] = a[a[rt].r].rsum[0];
if (a[a[rt].r].rsum[0] == a[a[rt].r].siz)
a[rt].rsum[0] += 1 + a[a[rt].l].rsum[0];
a[rt].sum[1] = a[a[rt].l].sum[1] + a[a[rt].r].sum[1];
a[rt].ans[1] = max(a[a[rt].l].ans[1], a[a[rt].r].ans[1]);
a[rt].lsum[1] = a[a[rt].l].lsum[1];
a[rt].rsum[1] = a[a[rt].r].rsum[1];
}
}
inline void push_down(int rt) {
if (a[rt].tag1 != -1) {
if (a[rt].l)
change(a[rt].l, a[rt].tag1);
if (a[rt].r)
change(a[rt].r, a[rt].tag1);
a[rt].tag1 = -1;
}
if (a[rt].tag2) {
if (a[rt].l)
change(a[rt].l, 2);
if (a[rt].r)
change(a[rt].r, 2);
a[rt].tag2 = false;
}
}
inline int merge(int rt1, int rt2) {
if (!rt1)
return rt2;
if (!rt2)
return rt1;
if (a[rt1].rank < a[rt2].rank) {
push_down(rt1);
a[rt1].r = merge(a[rt1].r, rt2);
push_up(rt1);
return rt1;
} else {
push_down(rt2);
a[rt2].l = merge(rt1, a[rt2].l);
push_up(rt2);
return rt2;
}
}
inline pair<int, int> split(int rt, int val) {
if (!rt)
return make_pair(0, 0);
push_down(rt);
if (a[a[rt].l].siz + 1 <= val) {
pair<int, int> ans = split(a[rt].r, val - a[a[rt].l].siz - 1);
a[rt].r = ans.first;
push_up(rt);
return make_pair(rt, ans.second);
} else {
pair<int, int> ans = split(a[rt].l, val);
a[rt].l = ans.second;
push_up(rt);
return make_pair(ans.first, rt);
}
}
} BST;
int n, m, opt, l, r;
int main() {
srand(time(0));
scanf("%d %d", &n, &m);
for (int i = 1, x; i <= n; i++)
scanf("%d", &x), BST.root = BST.merge(BST.root, BST.New(x));
while (m--) {
scanf("%d %d %d", &opt, &l, &r);
l++, r++;
if (!opt) { //由于对l~r操作所以我们把它们裂出来,再改该根节点并更新懒标记
pair<int, int> ans1 = BST.split(BST.root, r);
pair<int, int> ans2 = BST.split(ans1.first, l - 1);
BST.change(ans2.second, 0);
BST.root = BST.merge(BST.merge(ans2.first, ans2.second), ans1.second);
} else if (opt == 1) {
pair<int, int> ans1 = BST.split(BST.root, r);
pair<int, int> ans2 = BST.split(ans1.first, l - 1);
BST.change(ans2.second, 1);
BST.root = BST.merge(BST.merge(ans2.first, ans2.second), ans1.second);
} else if (opt == 2) {
pair<int, int> ans1 = BST.split(BST.root, r);
pair<int, int> ans2 = BST.split(ans1.first, l - 1);
BST.change(ans2.second, 2);
BST.root = BST.merge(BST.merge(ans2.first, ans2.second), ans1.second);
} else if (opt == 3) {
pair<int, int> ans1 = BST.split(BST.root, r);
pair<int, int> ans2 = BST.split(ans1.first, l - 1);
printf("%d\n", BST.a[ans2.second].sum[1]);
BST.root = BST.merge(BST.merge(ans2.first, ans2.second), ans1.second);
} else {
pair<int, int> ans1 = BST.split(BST.root, r);
pair<int, int> ans2 = BST.split(ans1.first, l - 1);
printf("%d\n", BST.a[ans2.second].ans[1]);
BST.root = BST.merge(BST.merge(ans2.first, ans2.second), ans1.second);
}
}
return 0;
}