POJ 3580 - SuperMemo - [伸展树splay]
题目链接:http://poj.org/problem?id=3580
Your friend, Jackson is invited to a TV show called SuperMemo in which the participant is told to play a memorizing game. At first, the host tells the participant a sequence of numbers, {A1, A2, ... An}. Then the host performs a series of operations and queries on the sequence which consists:
- ADD x y D: Add D to each number in sub-sequence {Ax ... Ay}. For example, performing "ADD 2 4 1" on {1, 2, 3, 4, 5} results in {1, 3, 4, 5, 5}
- REVERSE x y: reverse the sub-sequence {Ax ... Ay}. For example, performing "REVERSE 2 4" on {1, 2, 3, 4, 5} results in {1, 4, 3, 2, 5}
- REVOLVE x y T: rotate sub-sequence {Ax ... Ay} T times. For example, performing "REVOLVE 2 4 2" on {1, 2, 3, 4, 5} results in {1, 3, 4, 2, 5}
- INSERT x P: insert P after Ax. For example, performing "INSERT 2 4" on {1, 2, 3, 4, 5} results in {1, 2, 4, 3, 4, 5}
- DELETE x: delete Ax. For example, performing "DELETE 2" on {1, 2, 3, 4, 5} results in {1, 3, 4, 5}
- MIN x y: query the participant what is the minimum number in sub-sequence {Ax... Ay}. For example, the correct answer to "MIN 2 4" on {1, 2, 3, 4, 5} is 2
To make the show more interesting, the participant is granted a chance to turn to someone else that means when Jackson feels difficult in answering a query he may call you for help. You task is to watch the TV show and write a program giving the correct answer to each query in order to assist Jackson whenever he calls.
Input
The first line contains n (n ≤ 100000).
The following n lines describe the sequence.
Then follows M (M ≤ 100000), the numbers of operations and queries.
The following M lines describe the operations and queries.
Output
For each "MIN" query, output the correct answer.
Sample Input
5 1 2 3 4 5 2 ADD 2 4 1 MIN 4 5
Sample Output
5
题意:
给出五种操作:
- ADD x y D: 对区间[x,y]全部加上D;
- REVERSE x y: 反转区间[x,y];
- REVOLVE x y T: 将区间[x,y]向右循环平移T次,每次一格,例如 REVOLVE 2 4 2 在{1, 2, 3, 4, 5}操作结果为{1, 3, 4, 2, 5};
- INSERT x P: 在位置x后面插入一个数P;
- DELETE x: 删除位置x上的数;
- MIN x y: 求区间[x,y]中的最小值;
题解:
splay模板题,lazy标记模仿线段树标记区间加上多少,rev标记标记是否需要反转,mini[x]记录节点x统领的整棵子树(包含自己)的key[x]的最小值。
AC代码:
#include<cstdio> #include<algorithm> using namespace std; const int maxn=2e5+10; int n,m; int a[maxn]; /******************************** splay - st ********************************/ #define Key_value ch[ch[root][1]][0] int root,nodecnt; int par[maxn],ch[maxn][2]; int key[maxn],mini[maxn],size[maxn]; int lazy[maxn]; //lazy标记 bool rev[maxn]; //反转标记 int pool[maxn],poolsize; //节点回收 void NewNode(int &x,int p,int k) { if(poolsize>0) x=pool[--poolsize]; else x=++nodecnt; par[x]=p; ch[x][0]=ch[x][1]=0; key[x]=k; mini[x]=k; size[x]=1; lazy[x]=0; rev[x]=0; } void Update_Rev(int x) { if(x==0) return; swap(ch[x][0],ch[x][1]); rev[x]^=1; } void Update_Add(int x,int val) { if(x==0) return; key[x]+=val; mini[x]+=val; lazy[x]+=val; } void Pushup(int x) { size[x]=size[ch[x][0]]+size[ch[x][1]]+1; mini[x]=key[x]; if(ch[x][0]) mini[x]=min(mini[x],mini[ch[x][0]]); if(ch[x][1]) mini[x]=min(mini[x],mini[ch[x][1]]); } void Pushdown(int x) { if(rev[x]) { Update_Rev(ch[x][0]); Update_Rev(ch[x][1]); rev[x]=0; } if(lazy[x]) { Update_Add(ch[x][0],lazy[x]); Update_Add(ch[x][1],lazy[x]); lazy[x]=0; } } void Inorder(int x) //debug { if(x==0) return; Pushdown(x); Inorder(ch[x][0]); printf("%d ",key[x]); Inorder(ch[x][1]); Pushup(x); if(x==root) printf("\n"); } void Rotate(int x,int type) //旋转,0为左旋zag,1为右旋zig { int y=par[x]; Pushdown(y); Pushdown(x); //先把y的标记向下传递,再把x的标记往下传递 ch[y][!type]=ch[x][type]; par[ch[x][type]]=y; if(par[y]) ch[par[y]][(ch[par[y]][1]==y)]=x; par[x]=par[y]; ch[x][type]=y; par[y]=x; Pushup(y); Pushup(x); } void Splay(int x,int goal) { while(par[x]!=goal) { if(par[par[x]]==goal) Rotate(x,ch[par[x]][0]==x); //左孩子zig,右孩子zag else { Pushdown(par[par[x]]); Pushdown(par[x]); Pushdown(x); int y=par[x]; int type=(ch[par[y]][0]==y); //type=0,y是右孩子;type=1,y是左孩子 if(ch[y][type]==x) { Rotate(x,!type); Rotate(x,type); } else { Rotate(y,type); Rotate(x,type); } } } if(goal==0) root=x; } int Get_Kth(int x,int k) //得到第k个节点 { Pushdown(x); int t=size[ch[x][0]]+1; if(t==k) return x; if(t>k) return Get_Kth(ch[x][0],k); else return Get_Kth(ch[x][1],k-t); } int Get_Min(int x) { Pushdown(x); while(ch[x][0]) { x=ch[x][0]; Pushdown(x); } return x; } int Get_Max(int x) { Pushdown(x); while(ch[x][1]) { x=ch[x][1]; Pushdown(x); } return x; } void Build(int &x,int l,int r,int par) //建树,先建立中间结点,再建两端的方法 { if(l>r) return; int mid=(l+r)/2; NewNode(x,par,a[mid]); Build(ch[x][0],l,mid-1,x); Build(ch[x][1],mid+1,r,x); Pushup(x); } void Init() //初始化,前后各加一个空节点 { root=nodecnt=poolsize=0; par[root]=ch[root][0]=ch[root][1]=0; key[root]=size[root]=0; lazy[root]=rev[root]=0; NewNode(root,0,-1); //头部加入一个空位 NewNode(ch[root][1],root,-1); //尾部加入一个空位 Build(Key_value,1,n,ch[root][1]); Pushup(ch[root][1]); Pushup(root); } void Add(int l,int r,int val) { Splay(Get_Kth(root,l-1+1),0); //l的前驱l-1伸展到根 Splay(Get_Kth(root,r+1+1),root); //r的后继r+1伸展到根的右孩子 Update_Add(Key_value,val); Pushup(ch[root][1]); Pushup(root); } void Move(int l,int r,int p) //截取[l,r]放到位置p之后 { Splay(Get_Kth(root,l-1+1),0); //l的前驱l-1伸展到根 Splay(Get_Kth(root,r+1+1),root); //r的后继r+1伸展到根的右孩子 int tmp=Key_value; //Key_value=ch[ch[root][1]][0]所统领的子树即[l,r] Key_value=0; //剥离[l,r]子树 Pushup(ch[root][1]); Pushup(root); Splay(Get_Kth(root,p+0+1),0); //p伸展到根 Splay(Get_Kth(root,p+1+1),root); //p的后继p+1伸展到根的右孩子 Key_value=tmp; par[Key_value]=ch[root][1]; //接上[l,r]子树 Pushup(ch[root][1]); Pushup(root); } void Reverse(int l,int r) //反转[l,r]区间 { Splay(Get_Kth(root,l-1+1),0); Splay(Get_Kth(root,r+1+1),root); Update_Rev(Key_value); Pushup(ch[root][1]); Pushup(root); } void Revolve(int l,int r,int T) { int D=r-l+1; int L=T%D; if(L==0) return; Move(r-L+1,r,l-1); } void Insert(int p,int k) { Splay(Get_Kth(root,p+0+1),0); //p伸展到根 Splay(Get_Kth(root,p+1+1),root); //p的后继p+1伸展到根的右孩子 Inorder(Key_value); NewNode(Key_value,ch[root][1],k); Pushup(ch[root][1]); Pushup(root); } void Collect(int x) //回收节点x统领的子树 { if(x==0) return; pool[poolsize++]=x; Collect(ch[x][0]); Collect(ch[x][1]); } void Delete(int p) { Splay(Get_Kth(root,p-1+1),0); //p的前驱p-1伸展到根 Splay(Get_Kth(root,p+1+1),root); //p的后继p+1伸展到根的右孩子 Collect(Key_value); par[Key_value]=0; Key_value=0; Pushup(ch[root][1]); Pushup(root); } int Min(int l,int r) { Splay(Get_Kth(root,l-1+1),0); Splay(Get_Kth(root,r+1+1),root); return mini[Key_value]; } /******************************** splay - ed ********************************/ int main() { scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d",&a[i]); Init(); scanf("%d",&m); char op[50]; for(int i=1;i<=m;i++) { scanf("%s",op); if(op[0]=='A') { int x,y,D; scanf("%d%d%d",&x,&y,&D); Add(x,y,D); } if(op[0]=='R' && op[3]=='E') { int x,y; scanf("%d%d",&x,&y); Reverse(x,y); } if(op[0]=='R' && op[3]=='O') { int x,y,T; scanf("%d%d%d",&x,&y,&T); Revolve(x,y,T); } if(op[0]=='I') { int x,p; scanf("%d%d",&x,&p); Insert(x,p); } if(op[0]=='D') { int x; scanf("%d",&x); Delete(x); } if(op[0]=='M') { int x,y; scanf("%d%d",&x,&y); printf("%d\n",Min(x,y)); } //Inorder(root); } }
两组数据:
5
1 2 3 4 5 11
ADD 1 4 1
REVERSE 2 4
ADD 2 5 2
REVOLVE 2 4 2
DELETE 4
MIN 1 3
REVERSE 1 4
INSERT 2 4
REVOLVE 1 4 6
DELETE 2
MIN 1 4
10
1 2 3 4 5 6 7 8 9 10
15
ADD 4 8 3
MIN 5 7
MIN 7 10
REVERSE 2 5
MIN 2 6
MIN 2 3
INSERT 3 4
MIN 3 4
MIN 5 10
DELETE 6
MIN 3 5
MIN 4 4
REVOLVE 3 6 7
MIN 5 8
MIN 7 10
第一次写完之后一些BUG通过第一组数据调完了,感觉应该没问题了,然后交了发现WA,
然后用第二组数据一测,发现居然是因为用来debug的一些输出忘记删掉了……尴尬……