[Luogu] 排序机械臂
https://www.luogu.org/problemnew/solution/P3165
预处理
我们会发现一个问题:高度是无序的,而splay中要求有序,否则kth不能正确求解。
不需要求高度,只要求位置。
所以,用结构体存入 高度 与 下标,按高度排序,然后就可以把高度丢一边了(一波sao操作)。
记得头尾添加两个节点。。。
建树
正常 nlogn 可能会被卡常,所以,类似于线段树的建树,分此节点与左右儿子节点。
区间第k大
正常kth
区间翻转
首先,需要把所求的节点(即排序前下标为id的节点) splay 到 root 。
那么答案就是root左孩子的节点个数(因为有哨兵节点,所以+1-1抵消),记为s。
然后,取出 [i+1 , s+1] 这段区间,即:
将i节点 splay 到根,s+2节点 splay 到i的右节点。
再将s+2的左孩子打上翻转标记即可。
#include<iostream> #include<algorithm> #include<cstdio> #define MAXN 100010 #define MAX 999999999//最值 using namespace std; int n,size=1,root=0; struct node { int x,id; } b[MAXN]; namespace splay { struct Splay { int f,s,flag,son[2]; int v; } a[MAXN]; inline void clean(int rt) { //清空节点 a[rt].son[0]=a[rt].son[1]=a[rt].f=a[rt].s=a[rt].flag=a[rt].v=0; } inline void pushup(int rt) { //上传 if(!rt)return; a[rt].s=a[a[rt].son[0]].s+a[a[rt].son[1]].s+1; } inline void pushdown(int rt) { //标记下传 if(!rt||!a[rt].flag)return;//记得这句 a[a[rt].son[0]].flag^=1; a[a[rt].son[1]].flag^=1; a[rt].flag^=1; swap(a[rt].son[0],a[rt].son[1]); } inline void turn(int rt,int k) { //旋转 int x=a[rt].f,y=a[x].f; a[x].son[k^1]=a[rt].son[k]; if(a[rt].son[k])a[a[rt].son[k]].f=x; a[rt].f=y; if(y)a[y].son[a[y].son[1]==x]=rt; a[x].f=rt; a[rt].son[k]=x; pushup(x); pushup(rt); } void splay(int rt,int ancestry) { //伸展 while(a[rt].f!=ancestry) { int x=a[rt].f,y=a[x].f; pushdown(y); pushdown(x); pushdown(rt);//每次都要下传 if(y==ancestry)turn(rt,a[x].son[0]==rt); else { int k=a[y].son[0]==x?1:0; if(a[x].son[k]==rt) { turn(rt,k^1); turn(rt,k); } else { turn(x,k); turn(rt,k); } } } if(ancestry==0)root=rt; } inline int newnode(int x) { //建立新节点 int rt=size++; clean(rt); a[rt].v=x; a[rt].s=1; return rt; } int buildtree(int l,int r) { //建树 if(l>r)return 0; int mid=l+r>>1,lson=0,rson=0; lson=buildtree(l,mid-1); int rt=newnode(b[mid].x); rson=buildtree(mid+1,r); a[rt].son[0]=lson; a[rt].son[1]=rson; if(lson)a[lson].f=rt; if(rson)a[rson].f=rt; pushup(rt);//一定要有这句! return rt; } int kth(int rt,int k) { //第k大值 if(a[rt].s<k)return 0; while(1) { pushdown(rt);//下传 int y=a[rt].son[0]; if(k>a[y].s+1) { k-=a[y].s+1; rt=a[rt].son[1]; } else if(k<=a[y].s)rt=y; else return rt; } } inline void reverge(int i) { //区间翻转 splay(b[i].id+1,0);//记得加1(有哨兵节点) int s=a[a[root].son[0]].s; printf("%d ",s); int front=kth(root,i),next=kth(root,s+2); splay(front,0); splay(next,front); a[a[next].son[0]].flag^=1;//打上标记 } } inline int read() { int date=0,w=1; char c=0; while(c<'0'||c>'9') { if(c=='-')w=-1; c=getchar(); } while(c>='0'&&c<='9') { date=date*10+c-'0'; c=getchar(); } return date*w; } bool cmp(const node &x,const node &y) { if(x.x==y.x)return x.id<y.id; return x.x<y.x; } void init() { //预处理+读入+工作 n=read(); for(int i=1; i<=n; i++) { b[i].x=read(); b[i].id=i; } b[0].x=-MAX; b[0].id=1; b[n+1].x=MAX; b[n+1].id=n+1;//两个哨兵节点 sort(b+1,b+n+1,cmp); root=splay::buildtree(0,n+1); for(int i=1; i<=n-1; i++)splay::reverge(i); printf("%d\n",n);//最后一个一定是 n } int main() { init(); return 0; }