2437. Splay

题目链接

2437. Splay

给定一个长度为 n 的整数序列,初始时序列为 1,2,,n1,n

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

我们用 [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 次操作后的序列。

数据范围

1n,m105,
1lrn

输入样例:

6 3 2 4 1 5 3 5

输出样例:

5 2 1 4 3 6

解题思路

splay

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

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

  • 时间复杂度:O((n+m)×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; }

__EOF__

本文作者acwing_zyy
本文链接https://www.cnblogs.com/zyyun/p/16459949.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zyy2001  阅读(23)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示