洛谷传送门
题目描述
您需要写一种数据结构,来维护一个序列,其中需要提供以下操作(对于各个以往的历史版本):
- 在第 个数后插入数 。
- 删除第 个数。
- 翻转区间 ,例如原序列是 ,翻转区间 $[2,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
说明
强制在线:以下针对 的限制均是按位异或 后的限制。
对于 的数据, 。
对于另外 的数据, 。
对于 的数据, , 。
假设基于的历史版本的序列长度为
,有:
若
,则
。
若
,则
。
若
,则
。
若
,则
。
假设基于的历史版本的序列长度为
,有:
,
。
解题分析
可持久化 , 但是还卡空间。
注意到每次 前都会先 , 然后可能合并的链顶都已经复制过了, 所以在 的时候就不需要复制了。
代码如下:
#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;
}
}
}
}