1. 程式人生 > >HDU3487 Play With Chain [Splay]

HDU3487 Play With Chain [Splay]

form urn 一個數 original tin enc 再看 res flip

  題目傳送門

題目描述 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?
輸入格式 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.

輸出格式 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,3,...n,然後有兩種操作,一種是將一段區間取下來再接到另一個位置,另一種是區間反轉。   不難看出這是一道平衡樹的題目,題目中的區間反轉操作很明顯的是Splay,用下放標記的方法很容易實現。再看另一種操作,要求將一段數列截下來然後放入另一段中,這個其實可以旋轉以後把要移動的一段直接取下,然後在重新調整平衡樹,記錄要放入的區間的右端點,再把要放入的區間的左端點作為樹根,把截下來的一段直接放上去,再調整,把右端點也接上去就可以了。具體在代碼中註釋。   Code:   
  1 #include<cstdio>
  2 #include<cstring>
  3 #include<cstdlib>
  4 #include<cmath>
  5 #include<iomanip>
  6 #include<iostream>
  7 #include<algorithm>
  8 using namespace std;
  9 const int N=3e5+7;
 10 int n,m,root,tot;
 11 int cnt,ans[N];
 12 struct Node{
 13   int ch[2],val,fa;
 14   int size,mark;
 15   void add(int x,int father){
 16     mark=ch[0]=ch[1]=0;
 17     fa=father;val=x;size=1;}
 18 }t[N];
 19 inline int read()
 20 {
 21   char ch=getchar();int num=0;bool flag=false;
 22   while(ch<0||ch>9){if(ch==-)flag=true;ch=getchar();}
 23   while(ch>=0&&ch<=9){num=num*10+ch-0;ch=getchar();}
 24   return flag?-num:num;
 25 }
 26 inline void pushup(int x)
 27 {
 28   int l=t[x].ch[0],r=t[x].ch[1];
 29   t[x].size=t[l].size+t[r].size+1;
 30 }
 31 inline void pushdown(int x)
 32 {
 33   if(t[x].mark){
 34     t[t[x].ch[0]].mark^=1;
 35     t[t[x].ch[1]].mark^=1;
 36     swap(t[x].ch[0],t[x].ch[1]);
 37     t[x].mark=0;}
 38 }
 39 inline void rotate(int x)
 40 {
 41   int y=t[x].fa;
 42   int z=t[y].fa;
 43   int k=(t[y].ch[1]==x);
 44   t[z].ch[t[z].ch[1]==y]=x;
 45   t[x].fa=z;
 46   t[y].ch[k]=t[x].ch[k^1];
 47   t[t[x].ch[k^1]].fa=y;
 48   t[x].ch[k^1]=y;
 49   t[y].fa=x;
 50   pushup(y);pushup(x);
 51 }
 52 inline void splay(int x,int tag)
 53 {
 54   while(t[x].fa!=tag){
 55     int y=t[x].fa;
 56     int z=t[y].fa;
 57     if(z!=tag)
 58       (t[y].ch[1]==x)^(t[z].ch[1]==y)?
 59     rotate(x):rotate(y);
 60     rotate(x);}
 61   if(tag==0)root=x;
 62 }
 63 inline void insert(int x)
 64 {
 65   int now=root,fa=0;
 66   while(now)
 67     fa=now,now=t[now].ch[x>t[now].val];
 68   now=++tot;
 69   if(fa)t[fa].ch[x>t[fa].val]=now;
 70   t[now].add(x,fa);
 71   splay(now,0);
 72 }
 73 inline int find(int x)
 74 {
 75   int now=root;
 76   while(555){
 77     pushdown(now);
 78     if(t[t[now].ch[0]].size>=x)now=t[now].ch[0];
 79     else if(t[t[now].ch[0]].size+1==x)return now;
 80     else x-=(t[t[now].ch[0]].size+1),now=t[now].ch[1];
 81   }
 82 }
 83 inline int getmax(int x)
 84 {
 85   pushdown(x);
 86   while(t[x].ch[1]){
 87     x=t[x].ch[1];
 88     pushdown(x);}
 89   return x;
 90 }
 91 inline void merge(int x,int y)
 92 {
 93   t[x].ch[1]=y;
 94   t[y].fa=x;
 95 }
 96 inline void flip(int l,int r)
 97 {
 98   l=find(l),r=find(r+2);
 99   splay(l,0);splay(r,l);
100   t[t[t[root].ch[1]].ch[0]].mark^=1;
101 }
102 inline void cut(int x,int y,int z)
103 {
104   int l=find(x),r=find(y+2);
105   splay(l,0);splay(r,l);
106   //找到移動區間的左右端點的位置並調整至根節點
107   int root1=t[t[root].ch[1]].ch[0];
108   t[t[root].ch[1]].ch[0]=0;
109   //將該區間“截”下來
110   pushup(t[root].ch[1]);
111   pushup(root);
112   //調整節點信息
113   int neo=find(z+1);
114   splay(neo,0);
115   //找到放入區間的左端點並調整至根節點
116   int root2=t[root].ch[1];
117   //記錄下當前的右端點,因為待會兒合並時會將它直接去掉
118   merge(root,root1);
119   pushup(root);
120   //將截取區間接到當前根節點的右端點
121   int maxx=getmax(root);
122   splay(maxx,0);
123   //找到已經放入的區間的右端點並將其調整至根節點
124   merge(root,root2);
125   pushup(root);
126   //將之前被截取下來的端點和根節點
127 }
128 inline void travel(int now)
129 {
130   pushdown(now);
131   if(t[now].ch[0])travel(t[now].ch[0]);
132   if(t[now].val>1&&t[now].val<n+2)
133     ans[++cnt]=t[now].val-1;
134   if(t[now].ch[1])travel(t[now].ch[1]);
135 }
136 int main()
137 {
138   while(555){
139     n=read();m=read();
140     if(n<0||m<0)break;
141     root=tot=0;
142     for(int i=1;i<=n+2;i++)
143       insert(i);
144     char opt[5];int x,y,z;
145     for(int i=1;i<=m;i++){
146       scanf("%s",opt);
147       if(opt[0]==F){
148     x=read();y=read();
149     flip(x,y);}
150       else{
151     x=read();y=read();z=read();
152     cut(x,y,z);}
153     }
154     cnt=0;
155     travel(root);
156     for(int i=1;i<n;i++)
157       printf("%d ",ans[i]);
158     printf("%d\n",ans[n]);
159   }
160   return 0;

HDU3487 Play With Chain [Splay]