洛谷P3391 文艺平衡树 treap或splay

网址:https://www.luogu.org/problem/P3391

题意:

给出一个数列,$a_i=i$,维护数列的区间翻转,输出所有翻转后的结果。

题解:

一、treap解法:

涉及区间翻转的$treap$解法只能使用无旋$treap$,将无旋$treap$分裂成三棵子树,分别是区间左,区间和区间右,然后给区间打上标记然后交换子树,在$merge$的时候下传标记即可,由于$merge$的时候其他子树上的标记没有下传,所以$split$的时候也要先下传标记再$split$。平衡树的中序遍历输出的一定是原序列,所以最后我们只要中序遍历,输出的就是结果了。

AC代码:

#include 
using namespace std;
const int MAXN = 1e5 + 5;
struct Treap
{
	int val[MAXN], son[MAXN][2], rnk[MAXN], size[MAXN];
	bool tag[MAXN];
	int sz;
	void init()
	{
		sz = 0;
	}
	void pushup(int x)
	{
		size[x] = size[son[x][0]] + size[son[x][1]] + 1;
	}
	void pushdown(int x)
	{
		if (tag[x])
		{
			tag[x] = 0;
			tag[son[x][0]] ^= 1;
			tag[son[x][1]] ^= 1;
			swap(son[x][0], son[x][1]);
		}
	}
	int _rand()
	{
		static int seed = 12345;
		return seed = int(seed * 482711ll % 2147483647);
	}
	void merge(int& rt, int a, int b)
	{
		if (!a || !b)
		{
			rt = a + b;
			return;
		}
		if (rnk[a] < rnk[b])
		{
			pushdown(a), rt = a;
			merge(son[rt][1], son[a][1], b);
		}
		else
		{
			pushdown(b), rt = b;
			merge(son[rt][0], a, son[b][0]);
		}
		pushup(rt);
	}
	void split_sz(int rt, int& a, int& b, int s)
	{
		if (rt == 0)
		{
			a = b = 0;
			return;
		}
		pushdown(rt);
		if (size[son[rt][0]] < s)
		{
			a = rt;
			split_sz(son[rt][1], son[a][1], b, s - size[son[a][0]] - 1);
		}
		else
		{
			b = rt;
			split_sz(son[rt][0], a, son[b][0], s);
		}
		pushup(rt);
	}
	void split_v(int rt, int& a, int& b, int v)
	{
		if (rt == 0)
		{
			a = b = 0;
			return;
		}
		pushdown(rt);
		if (val[a] <= v)
			a = rt, split_v(son[rt][1], son[a][1], b, v);
		else
			b = rt, split_v(son[rt][0], a, son[b][0], v);
		pushup(rt);
	}
	int newnode(int x)
	{
		int rt = ++sz;
		val[rt] = x;
		rnk[rt] = _rand();
		son[rt][0] = son[rt][1] = 0;
		size[rt] = 1;
		tag[rt] = 0;
		return rt;
	}
	void insert(int& rt, int v)
	{
		int x = 0, y = 0, n = newnode(v);
		split_v(rt, x, y, v);
		merge(x, x, n);
		merge(rt, x, y);
	}
	void rerverse(int& rt, int l, int r)
	{
		int x = 0, y = 0, z = 0;
		split_sz(rt, x, y, r);
		split_sz(x, x, z, l - 1);
		tag[z] ^= 1;
		merge(x, x, z);
		merge(rt, x, y);
	}
	void print(int rt)
	{
		pushdown(rt);
		if (son[rt][0])
			print(son[rt][0]);
		printf("%d ", val[rt]);
		if (son[rt][1])
			print(son[rt][1]);
	}
};
Treap tr;
int p = 0;
int main()
{
	tr.init();
	int n, m;
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; ++i)
		tr.insert(p, i);
	int l, r;
	for (int i = 1; i <= m; ++i)
	{
		scanf("%d%d", &l, &r);
		tr.rerverse(p, l, r);
	}
	tr.print(p);
	putchar('\n');
	return 0;
}

二、splay解法:

$splay$的解法就是先把左端点的前驱$splay$到根,右端点的后驱$splay$到根的右儿子,显然根的左儿子就是需要翻转的区间,打标记即可,然后splay的时候先下传标记再翻转就可以了,由于$splay$旋转时旋转点上必须要有儿子,所以要加上无穷小和无穷大防止$splay$死循环,输出方法同$treap$。

AC代码:

// luogu-judger-enable-o2
#include 
using namespace std;
const int MAXN = 1e5 + 5;
struct Splay
{
	int size[MAXN], val[MAXN], fa[MAXN];
	int num[MAXN], son[MAXN][2], sta[MAXN];
	bool tag[MAXN];
	int sz, rt;
	void init()
	{
		rt = sz = 0;
	}
	void up(int x)
	{
		if (x)
		{
			size[x] = num[x];
			if (son[x][0])
				size[x] += size[son[x][0]];
			if (son[x][1])
				size[x] += size[son[x][1]];
		}
	}
	void down(int x)
	{
		if (tag[x])
		{
			tag[x] = 0;
			tag[son[x][0]] ^= 1;
			tag[son[x][1]] ^= 1;
			swap(son[x][0], son[x][1]);
		}
	}
	void con(int x, int y, int z)
	{
		if (x)
			fa[x] = y;
		if (y)
			son[y][z] = x;
	}
	int getson(int x)
	{
		return son[fa[x]][1] == x;
	}
	void rotate(int x)
	{
		int fx = fa[x], ffx = fa[fx];
		int fs = getson(x), ffs = getson(fx);
		con(son[x][fs ^ 1], fx, fs);
		con(fx, x, fs ^ 1);
		con(x, ffx, ffs);
		up(fx), up(x);
	}
	void splay(int x, int end)
	{
		end = fa[end];
		int top = 0;
		for (int f = x; f; f = fa[f])
			sta[++top] = f;
		for (int i = top; i; --i)
			down(sta[i]);
		int f;
		while (fa[x] != end)
		{
			f = fa[x];
			if (fa[f] != end)
				rotate(getson(x) == getson(f) ? f : x);
			rotate(x);
		}
		if (!end)
			rt = x;
	}
	int newnode(int x, int f)
	{
		int root = ++sz;
		val[root] = x;
		size[root] = num[root] = 1;
		son[root][0] = son[root][1] = 0;
		fa[root] = f;
		son[f][x > val[f]] = root;
		tag[root] = 0;
		return root;
	}
	void insert(int x)
	{
		if (!rt)
		{
			rt = newnode(x, 0);
			return;
		}
		int now = rt, f = 0;
		while (1)
		{
			if (x == val[now])
			{
				++num[now];
				up(now), up(f);
				splay(now, rt);
				return;
			}
			f = now, now = son[now][x > val[now]];
			if (!now)
			{
				int tmp = newnode(x, f);
				up(f);
				splay(tmp, rt);
				return;
			}
		}
	}
	int querynum(int rnk)
	{
		int ans = 0, now = rt;
		while (1)
		{
			down(now);
			if (son[now][0] && rnk <= size[son[now][0]])
			{
				now = son[now][0];
				continue;
			}
			if (son[now][0])
				rnk -= size[son[now][0]];
			if (rnk <= num[now])
			{
				splay(now, rt);
				return now;
			}
			rnk -= num[now];
			now = son[now][1];
			
		}
	}
	void reverse(int ql, int qr)
	{
		int l = querynum(ql), r = querynum(qr + 2);
		splay(l, rt), splay(r, son[l][1]);
		tag[son[r][0]] ^= 1;
	}
	void print(int x,int n)
	{
		down(x);
		if (son[x][0])
			print(son[x][0], n);
		if (val[x] > 1 && val[x] < n + 2)
			printf("%d ", val[x] - 1);
		if (son[x][1])
			print(son[x][1], n);
	}
};
Splay sp;
int main()
{
	int n, m;
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n + 2; ++i)
		sp.insert(i);
	int l, r;
	for (int i = 0; i < m; ++i)
	{
		scanf("%d%d", &l, &r);
		sp.reverse(l, r);
	}
	sp.print(sp.rt, n);
	printf("\n");
	return 0;
}

  

你可能感兴趣的:(洛谷P3391 文艺平衡树 treap或splay)