HDU3487 Play With Chain [Splay]
阿新 • • 發佈:2018-04-17
form urn 一個數 original tin enc 再看 res flip
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.
樣例 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:
題目傳送門
題目描述 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]