2437. Splay

题目链接

2437. Splay

给定一个长度为 \(n\) 的整数序列,初始时序列为 \(\\{1,2,…,n-1,n\\}\)

序列中的位置从左到右依次标号为 \(1 \sim n\)

我们用 \([l,r]\) 来表示从位置 \(l\) 到位置 \(r\) 之间(包括两端点)的所有数字构成的子序列。

现在要对该序列进行 \(m\) 次操作,每次操作选定一个子序列 \([l,r]\),并将该子序列中的所有数字进行翻转。

例如,对于现有序列 1 3 2 4 6 5 7,如果某次操作选定翻转子序列为 \([3,6]\),那么经过这次操作后序列变为 1 3 5 6 4 2 7

请你求出经过 \(m\) 次操作后的序列。

输入格式

第一行包含两个整数 \(n,m\)

接下来 \(m\) 行,每行包含两个整数 \(l,r\),用来描述一次操作。

输出格式

共一行,输出经过 \(m\) 次操作后的序列。

数据范围

\(1 \le n,m \le 10^5\),
\(1 \le l \le r \le n\)

输入样例:

6 3
2 4
1 5
3 5

输出样例:

5 2 1 4 3 6

解题思路

splay

\(splay\) 的关键在于 \(splay(x,k)\),表示将节点 \(x\) 放在节点 \(k\) 的下面,并且复杂度是 \(O(logn)\) 级别的,但要求按固定方式旋转,即在要将 \(x\) 左/右旋而上的情况下,如果 \(x-y-z\) 呈链状,则旋转两次 \(x\),否则先旋转 \(y\),再旋转 \(x\)。其次每次插入一个数后要求该数表示的节点 \(x\) 需为根节点,即执行 \(splay(x,0)\) 操作。这两种操作保证 \(splay\) 能够维持中序遍历。一般常见的有两种操作:插入和删除。例如删除区间 \([l,r]\),则可以找到 \(l-1\) 表示的节点 \(L\)\(r+1\) 表示的节点 \(R\),将 \(L\) 放在根节点上,即执行 \(splay(L,0)\) 操作,然后将 \(R\) 放在 \(L\) 下,即执行 \(splay(R,L)\) 操作,此时由于中序遍历,\(R\) 此时作为 \(L\) 的右子树,而 \([l,r]\) 表示的一些节点正好是 \(R\) 的左子树,删除左子树即删除区间 \([l,r]\),插入区间操作同理。

本题要求翻转 \(m\) 次选择的区间,可像线段树一样打懒标记,假设翻转 \([l,r]\) 的区间,即要求找到其表示的子树,则可以先找到 \(l-1\)\(r+1\) 表示的节点,但由于范围可能会越界,所以提前应该加上两个哨兵 \(0,n+1\),故此时应该找区间 \([0,n+1]\)\(l\)\(r+2\) 个数表示的节点 \(L\)\(R\),注意,这里表示的区间编号和节点编号是不同的,区间编号 \(i\) 对应该数在 \(splay\) 中序遍历下的 第 \(i\) 个数,即按中序遍历下的第 \(i\) 大的节点。找到后,将 \(L\) 置于根节点 \(splay(L,0)\),将 \(R\) 置于 \(L\) 下即其右子树 \(splay(R,L)\)\(R\) 的左子树其所求区间表示的节点,将其打上懒标记即可

  • 时间复杂度:\(O((n+m)\times logn)\)

代码

// Problem: Splay
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/2439/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=1e5+5;
int n,m,root,cnt;
struct Tr
{
	int s[2],p,v;
	int sz,flag;
	void init(int _p,int _v)
	{
		p=_p,v=_v;
	}
}tr[N];

void pushdown(int u)
{
	if(tr[u].flag)
	{
		swap(tr[u].s[0],tr[u].s[1]);
		tr[tr[u].s[0]].flag^=1;
		tr[tr[u].s[1]].flag^=1;
		tr[u].flag=0;
	}
}
void pushup(int u)
{
	tr[u].sz=1+tr[tr[u].s[0]].sz+tr[tr[u].s[1]].sz;
}
void rotate(int x)
{
	int y=tr[x].p,z=tr[y].p;
	int k=tr[y].s[1]==x;
	tr[z].s[tr[z].s[1]==y]=x,tr[x].p=z;
	tr[y].s[k]=tr[x].s[k^1],tr[tr[x].s[k^1]].p=y;
	tr[x].s[k^1]=y,tr[y].p=x;
	pushup(y),pushup(x);
}
void splay(int x,int k)
{
	while(tr[x].p!=k)
	{
		int y=tr[x].p,z=tr[y].p;
		if(z!=k)
		{
			if((tr[y].s[1]==x)^(tr[z].s[1]==y))rotate(x);
			else
				rotate(y);
		}
		rotate(x);
	}
	if(!k)root=x;
}
void insert(int v)
{
	int u=root,p=0;
	while(u)p=u,u=tr[u].s[v>tr[u].v];
	u=++cnt;
	if(p)tr[p].s[v>tr[p].v]=u;
	tr[u].init(p,v);
	splay(u,0);
}
int get_k(int k)
{
	int u=root;
	while(u)
	{
		pushdown(u);
		if(tr[tr[u].s[0]].sz>=k)u=tr[u].s[0];
		else if(tr[tr[u].s[0]].sz+1==k)return u;
		else
			k-=tr[tr[u].s[0]].sz+1,u=tr[u].s[1];
	}
	return -1;
}
void output(int u)
{
	pushdown(u);
	if(tr[u].s[0])output(tr[u].s[0]);
	if(tr[u].v>=1&&tr[u].v<=n)printf("%d ",tr[u].v);
	if(tr[u].s[1])output(tr[u].s[1]);
}


int main()
{
	scanf("%d%d",&n,&m);
	for(int i=0;i<=n+1;i++)insert(i);
	while(m--)
	{
		int l,r;
		scanf("%d%d",&l,&r);
		l=get_k(l),r=get_k(r+2);
		splay(l,0),splay(r,l);
		tr[tr[r].s[0]].flag^=1;
	}    
	output(root);
    return 0;
}
posted @ 2022-07-08 23:47  zyy2001  阅读(19)  评论(0编辑  收藏  举报