Step1 Problem:
HDU 3587
输入给出长 n 的一个序列,进行m次操作
1. 把序列中的第 a 个数字到第 b 个数字取出来插入到第 c 个数字后面
2. 翻转序列的 第 a 个数字到第 b 个数字这个区间
所有操作后输出结果
n <= 1e5 , m<=1e5
BZOJ 1251
输入给出长 n 的一个序列,进行m次操作
1. 把 [L, R] 这个区间的数都加上 V
2. 把 [L, R] 这个区间翻转
3. 求 [L, R] 这个区间的数的最值
n < 5e4,m<1e5
Step2 Ideas:
金桔的板子,东庆的注释,我就改了一点细微的格式,比较适合自己。
Step3 Code:
#include<bits/stdc++.h>
using namespace std;
const int N = 3e5+10;
//用来处理区间询问,每个节点维护的是一个子树的信息
//Splay可以将一段连续区间内的节点放到一颗子树内,所以这样可以维护一段区间的信息
struct Info {
int siz;
int ma;
Info(){};
Info(int x) {
siz = 1;
ma = x;
}
void addIt(int x) {
ma += x;
}
};
//区间(子树)信息的合并
Info operator+(const Info &l, const Info &r)
{
Info ret;
ret.siz = l.siz + r.siz;
ret.ma = max(l.ma, r.ma);
return ret;
}
int root;
//Splay的节点
struct node {
//son[0]是左儿子,son[1]是右儿子
int son[2], fa;
int val, lazy;
bool flp;
Info info;
int &l() {return son[0];}
int &r() {return son[1];}
node(int v = 0) {
l() = r() = fa = -1;
val = v;
info = Info(v);
lazy = 0;
flp = false;
}
//修改Splay上的节点后,也需要对 info 的信息进行维护
void addIt(int v) {
val += v;
lazy += v;
info.addIt(v);
}
void maintain();
void push_down();
}tree[N];
//进行 pushdown 操作,类似线段树
void node::push_down() {
if(lazy != 0) {
if(l() != -1) tree[l()].addIt(lazy);
if(r() != -1) tree[r()].addIt(lazy);
lazy = 0;
}
if(flp) {
swap(l(), r());
if(l() != -1) tree[l()].flp ^= 1;
if(r() != -1) tree[r()].flp ^= 1;
flp = false;
}
}
//Splay 进行旋转操作时,子树发生了改变,需要重新维护区间(子树)信息
void node::maintain() {
info = Info(val);
if(l() != -1) info = tree[l()].info + info;
if(r() != -1) info = info + tree[r()].info;
}
//查询当前节点是父亲的左儿子还是右儿子,左儿子返回0,右儿子返回1,如果无父亲返回-1
int ori(int st) {
int fa = tree[st].fa;
if(fa == -1) return -1;
return st == tree[fa].r();
}
//把 sn 变成 st 的儿子节点,如果 d 是 0 是左儿子,否则是右儿子
//这里子树发生了改变,需要重新维护 info 信息
void setc(int sn, int st, int d) {
if(st != -1) {
tree[st].son[d] = sn;
tree[st].maintain();
}
if(sn != -1) tree[sn].fa = st;
}
//进行旋转操作,这里需要自己画图理解一下,这里包括了左旋和右旋
void zg(int x) {
int st = tree[x].fa, p = -1;
tree[st].push_down();
tree[x].push_down();
int d = ori(x), dst = ori(st);
if(st != -1) p = tree[st].fa;
setc(tree[x].son[d^1], st, d);
setc(st, x, d^1);
setc(x, p, dst);
}
#define f(x) (tree[x].fa)
//将 x 旋转成 fa 的儿子,如果将 x 旋转成 根节点的话则不填 fa
void splay(int x, int fa = -1) {
//循环直到 x 是 fa 的儿子
while(f(x) != fa) {
//如果 fa 是 x 的爷爷,那么只需要一次旋转
if(f(f(x)) == fa) zg(x);
else {
//双旋!
//说明进行 zig zig 或者 zag zag 旋转
if(ori(x) == ori(f(x))) zg(f(x));
else zg(x);
zg(x);
}
}
//更新根节点
if(fa == -1) root = x;
}
int value[N];
int pos;
//要保证 value 有序,类似线段树建树,这样树高是 log(n) 的
int build(int l, int r) {
int st = pos++;
int m = (l+r)>>1;
tree[st] = node(value[m]);
if(l < m) setc(build(l, m-1), st, 0);
if(r > m) setc(build(m+1, r), st, 1);
return st;
}
int build(int n) {
pos = 0;
//添加 0 和 n + 1 两个虚拟节点,方便 cut 操作
return build(0, n+1);
}
//获得以 st 为根节点,中序遍历的第 v 个节点
int getid(int v, int st) {
tree[st].push_down();
int l = tree[st].l();
int lsiz = 1 + (l == -1 ? 0 : tree[l].info.siz);
if(v == lsiz) return st;
int d = v > lsiz;
if(d) v -= lsiz;
return getid(v, tree[st].son[d]);
}
int getseg(int l, int r) {
l--, r++;
//下标从 0 开始,需要找第 l + 1 个节点
l = getid(l+1, root), r = getid(r+1, root);
//现在 r+1 是 l-1 的父亲,那么 l-r 这一段子树肯定是 l-1 的右儿子
splay(r);
splay(l, r);
return tree[l].r();
}
void flip(int l, int r) {
int pos = getseg(l, r);
tree[pos].flp ^= 1;
}
void cut(int l, int r, int idx) {
//切下来 l - r 这段区间
int rootson1 = getseg(l, r);
int father = tree[rootson1].fa;
setc(-1, father, 1);
l = idx, r = idx+1;
//因为这里是虚拟节点,所以要多加一个 1
l = getid(l+1, root);
r = getid(r+1, root);
//将 idx+1 成为 idx 的父亲,那么上面切下来的区间放到idx的右边即可
splay(r);
splay(l, r);
setc(rootson1, l, 1);
}
int n, m;
int ans[N], cnt;
//中序遍历
void dfs(int u)
{
tree[u].push_down();
if(tree[u].son[0] != -1) dfs(tree[u].son[0]);
ans[cnt++] = tree[u].val;
if(tree[u].son[1] != -1) dfs(tree[u].son[1]);
}
int main()
{
char op[10];
while(~scanf("%d %d", &n, &m))
{
if(n==-1 && m ==-1) break;
for(int i = 1; i <= n; i++) value[i] = i;
root = build(n);
int l, r, idx;
while(m--)
{
scanf("%s", op);
if(op[0] == 'C') {
scanf("%d %d %d", &l, &r, &idx);
cut(l, r, idx);
}
else {
scanf("%d %d", &l, &r);
flip(l, r);
}
}
cnt = 0;
dfs(root);
for(int i = 1; i < cnt-1; i++)
{
if(i > 1) printf(" ");
printf("%d", ans[i]);
}
printf("\n");
}
return 0;
}