[Luogu P5055] 【模板】可持久化文艺平衡树

版权声明:欢迎转载蒟蒻博客,但请注明出处: https://blog.csdn.net/LPA20020220/article/details/85257149

洛谷传送门

题目描述

您需要写一种数据结构,来维护一个序列,其中需要提供以下操作(对于各个以往的历史版本):

  1. 在第 p p 个数后插入数 x x
  2. 删除第 p p 个数。
  3. 翻转区间 [ l , r ] [l,r] ,例如原序列是 { 5 , 4 , 3 , 2 , 1 } \{5,4,3,2,1\} ,翻转区间 $[2,4] $后,结果是 { 5 , 2 , 3 , 4 , 1 } \{5,2,3,4,1\}
  4. 查询区间 [ l , r ] [l,r] 中所有数的和。

和原本平衡树不同的一点是,每一次的任何操作都是基于某一个历史版本,同时生成一个新的版本(操作 4 4 即保持原版本无变化),新版本即编号为此次操作的序号。

本题强制在线。

输入输出格式

输入格式:

第一行包含一个正整数 N N ,表示操作的总数。

接下来 N N 行,每行前两个整数 v i , o p t i v_i,opt_i v i v_i 表示基于的过去版本号 ( 0 v i < i ) (0\le v_i<i) o p t i opt_i 表示操作的序号 ( 1 o p t i 4 ) (1\le opt_i\le 4)

o p t i = 1 opt_i=1 ,则接下来两个整数 p i , x i p_i,x_i ,表示操作为在第 p i p_i 个数后插入数 x x
o p t i = 2 opt_i=2 ,则接下来一个整数 p i p_i ,表示操作为删除第 p i p_i 个数。
o p t i = 3 opt_i=3 ,则接下来两个整数 l i , r i l_i,r_i ,表示操作为翻转区间 [ l i , r i ] [l_i,r_i]
o p t i = 4 opt_i=4 ,则接下来两个整数 l i , r i l_i,r_i ,表示操作为查询区间 [ l i , r i ] [l_i,r_i] 的和。

强制在线规则:
令当前操作之前的最后一次 4 4 操作的答案为 l a s t a n s lastans (如果之前没有 4 4 操作,则 l a s t a n s = 0 lastans=0 )。
则此次操作的 p i , x i p_i,x_i ​ 或 l i , r i l_i,r_i ​ 均按位异或上 l a s t a n s lastans 即可得到真实的 p i , x i p_i,x_i ​ 或 l i , r i l_i,r_i

输出格式:

对于每个序号为 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 的限制均是按位异或 l a s t a n s lastans 后的限制。

对于 30 % 30\% 的数据, N 5000 N\le 5000

对于另外 30 % 30\% 的数据, v i = i 1 v_i=i-1

对于 100 % 100\% 的数据, 1 N 2 × 1 0 5 1\le N\le 2\times 10^5 1 0 6 < x i < 1 0 6 -10^6<x_i<10^6

假设基于的历史版本的序列长度为 l e n 1 len\ge 1 ,有:
o p t i = 1 opt_i=1 ,则 0 p i l e n 0\le p_i\le len
o p t i = 2 opt_i=2 ,则 1 p i l e n 1\le p_i\le len
o p t i = 3 opt_i=3 ,则 1 l i r i l e n 1\le l_i\le r_i\le len
o p t i = 4 opt_i=4 ,则 1 l i r i l e n 1\le l_i\le r_i\le len

假设基于的历史版本的序列长度为 0 0 ,有:
o p t i = 1 opt_i=1 p i = 0 p_i=0

解题分析

可持久化 f h q   T r e a p fhq\ Treap , 但是还卡空间。

注意到每次 m e r g e merge 前都会先 s p l i t split , 然后可能合并的链顶都已经复制过了, 所以在 m e r g e 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;
			}
		}
	}
}

猜你喜欢

转载自blog.csdn.net/LPA20020220/article/details/85257149