洛谷传送门
题目描述
您需要写一种数据结构,来维护一个序列,其中需要提供以下操作(对于各个以往的历史版本):
- 在第 p p p 个数后插入数 x x x 。
- 删除第 p p p 个数。
- 翻转区间 [ l , r ] [l,r] [l,r],例如原序列是 { 5 , 4 , 3 , 2 , 1 } \{5,4,3,2,1\} {5,4,3,2,1},翻转区间 $[2,4] $后,结果是 { 5 , 2 , 3 , 4 , 1 } \{5,2,3,4,1\} {5,2,3,4,1}。
- 查询区间 [ l , r ] [l,r] [l,r] 中所有数的和。
和原本平衡树不同的一点是,每一次的任何操作都是基于某一个历史版本,同时生成一个新的版本(操作 4 4 4 即保持原版本无变化),新版本即编号为此次操作的序号。
本题强制在线。
输入输出格式
输入格式:
第一行包含一个正整数 N N N,表示操作的总数。
接下来 N N N 行,每行前两个整数 v i , o p t i v_i,opt_i vi,opti, v i v_i vi 表示基于的过去版本号 ( 0 ≤ v i < i ) (0\le v_i<i) (0≤vi<i), o p t i opt_i opti 表示操作的序号 ( 1 ≤ o p t i ≤ 4 ) (1\le opt_i\le 4) (1≤opti≤4)。
若
o
p
t
i
=
1
opt_i=1
opti=1,则接下来两个整数
p
i
,
x
i
p_i,x_i
pi,xi,表示操作为在第
p
i
p_i
pi 个数后插入数
x
x
x 。
若
o
p
t
i
=
2
opt_i=2
opti=2,则接下来一个整数
p
i
p_i
pi,表示操作为删除第
p
i
p_i
pi 个数。
若
o
p
t
i
=
3
opt_i=3
opti=3,则接下来两个整数
l
i
,
r
i
l_i,r_i
li,ri,表示操作为翻转区间
[
l
i
,
r
i
]
[l_i,r_i]
[li,ri]。
若
o
p
t
i
=
4
opt_i=4
opti=4,则接下来两个整数
l
i
,
r
i
l_i,r_i
li,ri,表示操作为查询区间
[
l
i
,
r
i
]
[l_i,r_i]
[li,ri] 的和。
强制在线规则:
令当前操作之前的最后一次
4
4
4 操作的答案为
l
a
s
t
a
n
s
lastans
lastans(如果之前没有
4
4
4 操作,则
l
a
s
t
a
n
s
=
0
lastans=0
lastans=0)。
则此次操作的
p
i
,
x
i
p_i,x_i
pi,xi 或
l
i
,
r
i
l_i,r_i
li,ri 均按位异或上
l
a
s
t
a
n
s
lastans
lastans 即可得到真实的
p
i
,
x
i
p_i,x_i
pi,xi 或
l
i
,
r
i
l_i,r_i
li,ri。
输出格式:
对于每个序号为 4 4 4 的查询操作,输出一行一个数表示区间的和。
输入输出样例
输入样例#1:
10
0 1 0 1
1 1 1 2
2 4 1 2
3 1 2 0
4 4 2 1
5 3 5 7
6 4 5 6
4 1 7 1
8 3 4 6
9 4 4 1
输出样例#1:
3
4
5
10
说明
强制在线:以下针对 p i , x i , l i , r i p_i,x_i,l_i,r_i pi,xi,li,ri 的限制均是按位异或 l a s t a n s lastans lastans 后的限制。
对于 30 % 30\% 30% 的数据, N ≤ 5000 N\le 5000 N≤5000。
对于另外 30 % 30\% 30% 的数据, v i = i − 1 v_i=i-1 vi=i−1。
对于 100 % 100\% 100% 的数据, 1 ≤ N ≤ 2 × 1 0 5 1\le N\le 2\times 10^5 1≤N≤2×105, − 1 0 6 < x i < 1 0 6 -10^6<x_i<10^6 −106<xi<106。
假设基于的历史版本的序列长度为
l
e
n
≥
1
len\ge 1
len≥1,有:
若
o
p
t
i
=
1
opt_i=1
opti=1,则
0
≤
p
i
≤
l
e
n
0\le p_i\le len
0≤pi≤len。
若
o
p
t
i
=
2
opt_i=2
opti=2,则
1
≤
p
i
≤
l
e
n
1\le p_i\le len
1≤pi≤len。
若
o
p
t
i
=
3
opt_i=3
opti=3,则
1
≤
l
i
≤
r
i
≤
l
e
n
1\le l_i\le r_i\le len
1≤li≤ri≤len。
若
o
p
t
i
=
4
opt_i=4
opti=4,则
1
≤
l
i
≤
r
i
≤
l
e
n
1\le l_i\le r_i\le len
1≤li≤ri≤len。
假设基于的历史版本的序列长度为
0
0
0,有:
o
p
t
i
=
1
opt_i=1
opti=1,
p
i
=
0
p_i=0
pi=0。
解题分析
可持久化 f h q T r e a p fhq\ Treap fhq Treap, 但是还卡空间。
注意到每次 m e r g e merge merge前都会先 s p l i t split split, 然后可能合并的链顶都已经复制过了, 所以在 m e r g e merge merge的时候就不需要复制了。
代码如下:
#include <cstdio>
#include <cstring>
#include <cmath>
#include <ctime>
#include <climits>
#include <cctype>
#include <cstdlib>
#include <algorithm>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 200050
#define ll long long
template <class T>
IN void in(T &x)
{
static char c; static bool neg;
x = 0; c = gc;
for (; !isdigit(c); c = gc)
if (c == '-') neg = true;
for (; isdigit(c); c = gc)
x = (x << 1) + (x << 3) + c - 48;
if (neg) neg = false, x = -x;
}
int n, cnt;
ll lastans;
int root[MX];
struct Node {int val, son[2], siz, key; ll sum; bool rev;} tree[MX * 50];
IN int rd() {return (1ll * rand() * rand() % INT_MAX + rand()) % INT_MAX;}
namespace Fhq
{
#define ls tree[now].son[0]
#define rs tree[now].son[1]
IN void pushup(R int now)
{
tree[now].siz = 1 + tree[ls].siz + tree[rs].siz;
tree[now].sum = tree[now].val + tree[ls].sum + tree[rs].sum;
}
IN void pushdown(R int now)
{
if (tree[now].rev)
{
std::swap(ls, rs);
if (ls)
{
tree[++cnt] = tree[ls];
ls = cnt; tree[cnt].rev ^= 1;
}
if (rs)
{
tree[++cnt] = tree[rs];
rs = cnt; tree[cnt].rev ^= 1;
}
tree[now].rev = false;
}
}
IN void split(R int now, R int tar, int &x, int &y)
{
if (!now) return x = y = 0, void();
pushdown(now);
if (tree[ls].siz >= tar)
{
y = ++cnt; tree[y] = tree[now];
split(ls, tar, x, tree[y].son[0]);
pushup(y);
}
else
{
x = ++cnt; tree[x] = tree[now];
split(rs, tar - tree[ls].siz - 1, tree[x].son[1], y);
pushup(x);
}
}
int merge(R int x, R int y)
{
if ((!x) || (!y)) return x + y;
if (tree[x].key < tree[y].key)
{
pushdown(x);
tree[x].son[1] = merge(tree[x].son[1], y);
pushup(x); return x;
}
else
{
pushdown(y);
tree[y].son[0] = merge(x, tree[y].son[0]);
pushup(y); return y;
}
}
IN void insert(int &root, R int pos, R int val)
{
int nw;
tree[nw = ++cnt] = {val, {0, 0}, 1, rd(), val, false};
int x = 0, y = 0; split(root, pos, x, y); root = merge(merge(x, nw), y);
}
IN void erase(int &root, R int pos)
{
int x = 0, y = 0, z = 0;
split(root, pos, x, y);
split(x, pos - 1, x, z);
root = merge(x, y);
}
IN void rev(int &root, R int lef, R int rig)
{
int x = 0, y = 0, z = 0;
split(root, rig, x, z);
split(x, lef - 1, x, y);
tree[y].rev ^= 1;
root = merge(merge(x, y), z);
}
IN ll getsum(int &root, R int lef, R int rig)
{
int x = 0, y = 0, z = 0;
split(root, rig, x, z);
split(x, lef - 1, x, y);
ll ret = tree[y].sum;
root = merge(merge(x, y), z);
return ret;
}
#undef ls
#undef rs
}
int main(void)
{
using namespace Fhq;
in(n); int base, op;
ll foo, bar;
for (R int i = 1; i <= n; ++i)
{
in(base), in(op); root[i] = root[base]; in(foo), foo ^= lastans;
switch(op)
{
case 1:
{
in(bar), bar ^= lastans;
insert(root[i], foo, bar); break;
}
case 2: erase(root[i], foo); break;
case 3: in(bar), bar ^= lastans, rev(root[i], foo, bar); break;
case 4:
{
in(bar), bar ^= lastans;
printf("%lld\n", lastans = getsum(root[i], foo, bar)); break;
}
}
}
}