Hdu3487-Play with Chain(伸展树分裂合并)
Problem Description
YaoYao is fond of playing his chains. He has a chain containing n diamonds on it. Diamonds are numbered from 1 to n. At first, the diamonds on the chain is a sequence: 1, 2, 3, …, n. He will perform two types of operations: CUT a b c: He will first cut down the chain from the ath diamond to the bth diamond. And then insert it after the cth diamond on the remaining chain. For example, if n=8, the chain is: 1 2 3 4 5 6 7 8; We perform “CUT 3 5 4”, Then we first cut down 3 4 5, and the remaining chain would be: 1 2 6 7 8. Then we insert “3 4 5” into the chain before 5th diamond, the chain turns out to be: 1 2 6 7 3 4 5 8.
FLIP a b: We first cut down the chain from the ath diamond to the bth diamond. Then reverse the chain and put them back to the original position. For example, if we perform “FLIP 2 6” on the chain: 1 2 6 7 3 4 5 8. The chain will turn out to be: 1 4 3 7 6 2 5 8
He wants to know what the chain looks like after perform m operations. Could you help him?
FLIP a b: We first cut down the chain from the ath diamond to the bth diamond. Then reverse the chain and put them back to the original position. For example, if we perform “FLIP 2 6” on the chain: 1 2 6 7 3 4 5 8. The chain will turn out to be: 1 4 3 7 6 2 5 8
He wants to know what the chain looks like after perform m operations. Could you help him?
Input
There will be multiple test cases in a test data.
For each test case, the first line contains two numbers: n and m (1≤n, m≤3*100000), indicating the total number of diamonds on the chain and the number of operations respectively. Then m lines follow, each line contains one operation. The command is like this: CUT a b c // Means a CUT operation, 1 ≤ a ≤ b ≤ n, 0≤ c ≤ n-(b-a+1). FLIP a b // Means a FLIP operation, 1 ≤ a < b ≤ n. The input ends up with two negative numbers, which should not be processed as a case.
For each test case, the first line contains two numbers: n and m (1≤n, m≤3*100000), indicating the total number of diamonds on the chain and the number of operations respectively. Then m lines follow, each line contains one operation. The command is like this: CUT a b c // Means a CUT operation, 1 ≤ a ≤ b ≤ n, 0≤ c ≤ n-(b-a+1). FLIP a b // Means a FLIP operation, 1 ≤ a < b ≤ n. The input ends up with two negative numbers, which should not be processed as a case.
Output
For each test case, you should print a line with n numbers. The ith number is the number of the ith diamond on the chain.
Sample Input
8 2
CUT 3 5 4
FLIP 2 6
-1 -1
Sample Output
1 4 3 7 6 2 5 8
题意:起初整个排列是1,2,,,N,然后有两种操作
CUT a b c 将[a,b]分离出来后插入到剩余的数的第c个位置之后
FLIP a b 将[a,b]翻转
求最后的排列。
解析:伸展树合并分裂。对于CUT操作,先把左边和右边的分裂出来,再合并在一起,再分成[1,c]和[c+1,k](k是剩余的个数,注意区间可能为空,特殊处理一下就好了),
再将[a,b]插入进去。对于FLIP操作,还是分裂,再将[a,b]翻转。最后将所有翻转标记下压。记录答案即可。
代码
#include<cstdio> #include<cstring> #include<cstdlib> #include<algorithm> using namespace std; const int INF=1e9+7; const int maxn=300005; int N,M,ans,A[maxn],cnt; //A数组保存数,cnt是节点标号,我是用数组模拟的 struct treap { treap* son[2]; //左右儿子 int v,s,rev; treap(){ v=s=rev=0; son[0]=son[1]=NULL; } treap(int nv); int rk(){ return son[0]->s+1; } //排名,第几个数 int cmp(int k) //比较,如果相等返回-1,小于返回0,大于1 { if(k==rk()) return -1; return k<rk()?0:1; } void pushup(){ s=son[0]->s+son[1]->s+1; } //更新大小 void pushdown(); //处理懒惰标记 }null,tr[maxn]; treap::treap(int nv) { v=nv; s=1; rev=0; son[0]=son[1]=&null; } void treap::pushdown() { if(this==&null) return; if(rev) { swap(son[0],son[1]); son[0]->rev^=1; son[1]->rev^=1; rev=0; } } treap* NewNode(int x) { tr[cnt]=treap(x); return tr+cnt++; } struct splaytree { int Size; treap* root; splaytree(){ Size=0; root=&null; } void Rotate(treap* &t,int d) //翻转操作 { t->pushdown(); treap* p=t->son[d^1]; p->pushdown(); t->son[d^1]=p->son[d]; p->son[d]=t; t->pushup(); t=p; t->pushup(); } void Splay(treap* &t,int k) //将第k大的节点伸展到根 { t->pushdown(); int d=t->cmp(k); if(d!=-1) { if(d) Splay(t->son[d],k- t->rk()); else Splay(t->son[d],k); Rotate(t,d^1); } t->pushup(); } void Build(treap* &t,int le,int ri) //将N个数建成一棵树 { if(le>ri) return; int mid=(le+ri)/2; t=NewNode(mid); Build(t->son[0],le,mid-1); Build(t->son[1],mid+1,ri); t->pushup(); } void Cut(treap* &t,int a,int b,int c) { int len=b-a+1; if(len==N) return; //是整个区间就不用管了 Splay(t,a); t->pushdown(); //分裂出左边的 treap *L=t->son[0]; L->pushdown(); t->son[0]=&null; t->pushup(); Splay(t,len); t->pushdown(); //分裂出右边的 treap *R=t->son[1]; R->pushdown(); t->son[1]=&null; t->pushup(); treap *nt; if(R!=&null) //左右合并 { nt=R; Splay(nt,1); nt->son[0]=L; nt->pushup(); } else { nt=L; Splay(nt,a-1); nt->son[1]=R; nt->pushup(); } if(c+len==N) //在整个之后特殊处理一下就好 { Splay(nt,c); Splay(t,1); t->son[0]=nt; t->pushup(); return; } Splay(nt,c+1); treap *l=nt->son[0]; l->pushdown(); nt->son[0]=&null; nt->pushup(); t->son[1]=nt; t->pushup(); Splay(t,1); t->son[0]=l; t->pushup(); } void Reverse(treap* &t,int a,int b) //翻转 { Splay(t,a); //左边 treap *L=t->son[0]; L->pushdown(); t->son[0]=&null; t->pushup(); Splay(t,b-a+1); //右边 treap *R=t->son[1]; R->pushdown(); t->son[1]=&null; t->pushup(); t->rev^=1; //置翻转标记 t->pushdown(); t->son[0]=L; t->pushup(); Splay(t,b); t->son[1]=R; t->pushup(); } void PushAll(treap* &t) //中序遍历 { if(t==&null) return; t->pushdown(); PushAll(t->son[0]); A[++ans]=t->v; PushAll(t->son[1]); t->pushup(); } }; int main() { while(scanf("%d%d",&N,&M)!=EOF) { if(N<0&&M<0) break; splaytree spt; cnt=0; spt.Build(spt.root,1,N); //建树 int a,b,c; char op[10]; while(M--) { scanf("%s",op); if(op[0]=='C') //CUT操作 { scanf("%d%d%d",&a,&b,&c); spt.Cut(spt.root,a,b,c); } else //FLIP操作 { scanf("%d%d",&a,&b); spt.Reverse(spt.root,a,b); } } ans=0; spt.PushAll(spt.root); //整个下压 for(int i=1;i<=ans;i++) printf("%d%c",A[i],i==ans?'\n':' '); } return 0; }