伸展樹是比較神奇的,它可以做很多線段樹不能實現的事情。
最近做伸展樹做了好長時間了,現在重新把題目整理下,代碼統一些一下呢。說明多是含在代碼的注釋中。
剛開始學,可以看論文,然后按照別人的代碼去寫。
我是參照cxlove大神學習的:http://blog.csdn.net/acm_cxlove/article/details/7815019
還有HH的:http://www.notonlysuccess.com/index.php/splay-tree/
學習算法只有經過自己不斷寫了才能完全掌握,代碼風格也要適合自己的。
1、POJ 3468 A Simple Problem with Integers (成段更新、區間求和)
Time Limit: 5000MS | Memory Limit: 131072K | |
Total Submissions: 41977 | Accepted: 12207 | |
Case Time Limit: 2000MS |
Description
You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval.
Input
The first line contains two numbers N and Q. 1 ≤ N,Q ≤ 100000.
The second line contains N numbers, the initial values of A1, A2, ... , AN. -1000000000 ≤ Ai ≤ 1000000000.
Each of the next Q lines represents an operation.
"C a b c" means adding c to each of Aa, Aa+1, ... , Ab. -10000 ≤ c ≤ 10000.
"Q a b" means querying the sum of Aa, Aa+1, ... , Ab.
Output
You need to answer all Q commands in order. One answer in a line.
Sample Input
10 5 1 2 3 4 5 6 7 8 9 10 Q 4 4 Q 1 10 Q 2 4 C 3 6 3 Q 2 4
Sample Output
4 55 9 15
Hint
Source

/* * POJ 3468 A Simple Problem with Integers * 經典的線段樹題目,用splay tree來作為入門題 * 成段更新+區間求和 * 題目給定了n個數A1,A2,...An,有以下兩種操作 * C a b c:把c加入到Aa,Aa+1,..Ab中 * Q a b:查詢Aa,Aa+1,..Ab的和 * 需要的變量:pre,ch,size(這三個基本都要),key(保存結點的值),sum(子樹值和),add(增量的標記) * (一般標記類,正確的做法都是要先更新掉該點,標記是標記沒有更新子結點) */ #include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define Key_value ch[ch[root][1]][0] const int MAXN=100010; int pre[MAXN],ch[MAXN][2],size[MAXN],root,tot1;//父結點、左右孩子、子樹規模、根結點、結點數量 int key[MAXN];//該點的值 int add[MAXN];//增量的延遲標記 long long sum[MAXN];//子樹的和 int s[MAXN],tot2;//內存池、內存池容量(這題用不到,如果有刪除操作,內存不夠可以這樣 int a[MAXN];//初始的數組,建樹時候用 int n,q; //debug部分 void Treavel(int x) { if(x) { Treavel(ch[x][0]); printf("結點%2d:左兒子 %2d 右兒子 %2d 父結點 %2d size=%2d,key=%2d add=%2d sum=%I64d\n",x,ch[x][0],ch[x][1],pre[x],size[x],key[x],add[x],sum[x]); Treavel(ch[x][1]); } } void debug() { printf("root:%d\n",root); Treavel(root); } //以上是debug void NewNode(int &r,int father,int k)//一個是調用的時候注意變量順序,還有r必須引用& { if(tot2)r=s[tot2--];//取得時候是tot2--,那么存的時候就要是++tot2 else r=++tot1; pre[r]=father; size[r]=1;//這個不能忘記 ,一定是1,否則可能出錯 key[r]=k; add[r]=0; sum[r]=0; ch[r][0]=ch[r][1]=0; } //給r為根的子樹增加值,一定把當前結點的全部更新掉,再加個延遲標記表示兒子結點沒有更新 void Update_Add(int r,int ADD) { if(r==0)return; add[r]+=ADD; key[r]+=ADD; sum[r]+=(long long)ADD*size[r]; } //通過孩子結點更新父親結點 void Push_Up(int r) { size[r]=size[ch[r][0]]+size[ch[r][1]]+1; sum[r]=sum[ch[r][0]]+sum[ch[r][1]]+key[r]; } //將延遲標記更新到孩子結點 void Push_Down(int r) { if(add[r]) { Update_Add(ch[r][0],add[r]); Update_Add(ch[r][1],add[r]); add[r]=0; } } //建樹 //先建立中間結點,再兩端的方法 void Build(int &x,int l,int r,int father) { if(l>r)return; int mid=(l+r)/2; NewNode(x,father,a[mid]); Build(ch[x][0],l,mid-1,x); Build(ch[x][1],mid+1,r,x); Push_Up(x); } //初始化,前后各加一個king結點 void Init() { for(int i=1;i<=n;i++)scanf("%d",&a[i]); root=tot1=tot2=0; ch[root][0]=ch[root][1]=pre[root]=size[root]=add[root]=sum[root]=0; key[root]=0; NewNode(root,0,-1); NewNode(ch[root][1],root,-1);//頭尾各加入一個空位 Build(Key_value,1,n,ch[root][1]); Push_Up(ch[root][1]); Push_Up(root); } //旋轉,0為左旋,1為右旋 該部分基本固定 void Rotate(int x,int kind) { int y=pre[x]; Push_Down(y); Push_Down(x);//先把y的標記向下傳遞,再把x的標記往下傳遞 ch[y][!kind]=ch[x][kind]; pre[ch[x][kind]]=y; if(pre[y]) ch[pre[y]][ch[pre[y]][1]==y]=x; pre[x]=pre[y]; ch[x][kind]=y; pre[y]=x; Push_Up(y);//維護y結點 } //Splay調整,將結點r調整到goal下面 void Splay(int r,int goal) { Push_Down(r); while(pre[r]!=goal) { if(pre[pre[r]]==goal) Rotate(r,ch[pre[r]][0]==r); else { int y=pre[r]; int kind=ch[pre[y]][0]==y; if(ch[y][kind]==r) { Rotate(r,!kind); Rotate(r,kind); } else { Rotate(y,kind); Rotate(r,kind); } } } Push_Up(r); if(goal==0)root=r; } //得到第k個結點 int Get_Kth(int r,int k) { Push_Down(r); int t=size[ch[r][0]]+1; if(t==k)return r; if(t>k)return Get_Kth(ch[r][0],k); else return Get_Kth(ch[r][1],k-t); } int Get_Min(int r) { Push_Down(r); while(ch[r][0]) { r=ch[r][0]; Push_Down(r); } return r; } int Get_Max(int r) { Push_Down(r); while(ch[r][1]) { r=ch[r][1]; Push_Down(r); } return r; } //區間增加一個值 //注意因為在前面增加了個結點,所以把第l個結點旋轉到根結點,第r+2個結點旋轉到根結點的右孩子, //那么Key_value(ch[ch[root][1]][0]剛好就是區間[l,r] void ADD(int l,int r,int D) { Splay(Get_Kth(root,l),0);//第l個點到根結點 Splay(Get_Kth(root,r+2),root);//第r+2個點到根結點的右孩子 Update_Add(Key_value,D); Push_Up(ch[root][1]); Push_Up(root); } //查詢區間的和 long long Query_Sum(int l,int r) { Splay(Get_Kth(root,l),0);//第l個點到根結點 Splay(Get_Kth(root,r+2),root);//第r+2個點到根結點的右孩子 return sum[Key_value]; } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); while(scanf("%d%d",&n,&q)==2) { Init();//這個不能忘記 while(q--) { char op[20]; int x,y,z; scanf("%s",op); if(op[0]=='Q') { scanf("%d%d",&x,&y); printf("%I64d\n",Query_Sum(x,y)); } else { scanf("%d%d%d",&x,&y,&z); ADD(x,y,z); } } } return 0; }
2、營業額統計(一個一個數插入,找出和這個數最接近的)
營業額統計
Description
營業額統計 Tiger最近被公司升任為營業部經理,他上任后接受公司交給的第一項任務便是統計並分析公司成立以來的營業情況。 Tiger拿出了公司的賬本,賬本上記錄了公司成立以來每天的營業額。分析營業情況是一項相當復雜的工作。由於節假日,大減價或者是其他情況的時候,營業額會出現一定的波動,當然一定的波動是能夠接受的,但是在某些時候營業額突變得很高或是很低,這就證明公司此時的經營狀況出現了問題。經濟管理學上定義了一種最小波動值來衡量這種情況: 該天的最小波動值 當最小波動值越大時,就說明營業情況越不穩定。 而分析整個公司的從成立到現在營業情況是否穩定,只需要把每一天的最小波動值加起來就可以了。你的任務就是編寫一個程序幫助Tiger來計算這一個值。 第一天的最小波動值為第一天的營業額。 l 輸入輸出要求
Input
第一行為正整數 ,表示該公司從成立一直到現在的天數,接下來的n行每行有一個正整數 ,表示第i天公司的營業額。
Output
輸出文件僅有一個正整數,即Sigma(每天最小的波動值) 。結果小於2^31 。
Sample Input
6
5
1
2
5
4
6
Sample Output
12
Hint
結果說明:5+|1-5|+|2-1|+|5-5|+|4-5|+|6-5|=5+4+1+0+1+1=12
這道題目很其他的比起來太簡單了。。。
隨便寫、、

/* * 這題的意思就是每插入一個數,累加和前面插入的數的差值。第一個數加本身。 * 用splay tree找尋前驅和后繼。 * 但是這題的splay tree按照key值得大小建立。 * 我的做法是把這個數插入進去,找前驅和后繼,其中一個就是最接近的了。 * 這樣做有了重復的元素,但是沒有影響的 * 這種題目用STL的set 、 SBT等都很好過了 */ #include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; const int INF=0x3f3f3f3f; const int MAXN=1000010; int pre[MAXN],ch[MAXN][2],key[MAXN]; int root,tot1; void NewNode(int &r,int father,int k) { r=++tot1; pre[r]=father; ch[r][0]=ch[r][1]=0; key[r]=k; } void Init() { root=tot1=0; ch[root][0]=ch[root][1]=key[root]=pre[root]=0; } //旋轉 void Rotate(int x,int kind) { int y=pre[x]; ch[y][!kind]=ch[x][kind]; pre[ch[x][kind]]=y; if(pre[y]) ch[pre[y]][ch[pre[y]][1]==y]=x; pre[x]=pre[y]; ch[x][kind]=y; pre[y]=x; } //Splay調整 void Splay(int r,int goal) { while(pre[r]!=goal) { if(pre[pre[r]]==goal) Rotate(r,ch[pre[r]][0]==r); else { int y=pre[r]; int kind=ch[pre[y]][0]==y; if(ch[y][kind]==r) { Rotate(r,!kind); Rotate(r,kind); } else { Rotate(y,kind); Rotate(r,kind); } } } if(goal==0)root=r; } void Insert(int k)//插入一個值為k的結點 { int r=root; if(r==0) { NewNode(root,0,k); return; } while(ch[r][key[r]<k]) { r=ch[r][key[r]<k]; } NewNode(ch[r][key[r]<k],r,k); Splay(ch[r][key[r]<k],0); } int Get_Min(int r) { while(ch[r][0]) { r=ch[r][0]; } return r; } int Get_Max(int r) { while(ch[r][1]) { r=ch[r][1]; } return r; } int main() { int n; scanf("%d",&n); int num; int ans=0; for(int i=1;i<=n;i++) { if(scanf("%d",&num)==EOF)num=0; Insert(num); if(i==1) { ans+=num; } else { int tmp=INF; if(ch[root][0]) tmp=min(tmp,key[root]-key[Get_Max(ch[root][0])]); if(ch[root][1]) tmp=min(tmp,key[Get_Min(ch[root][1])]-key[root]); ans+=tmp; } } printf("%d\n",ans); return 0; }
3、HDU 1890 Robotic Sort(區間反轉、刪除根結點)
Robotic Sort
Time Limit: 6000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1403 Accepted Submission(s): 592
In this task, you are to write software for a robot that handles samples in such a laboratory. Imagine there are material samples lined up on a running belt. The samples have different heights, which may cause troubles to the next processing unit. To eliminate such troubles, we need to sort the samples by their height into the ascending order.
Reordering is done by a mechanical robot arm, which is able to pick up any number of consecutive samples and turn them round, such that their mutual order is reversed. In other words, one robot operation can reverse the order of samples on positions between A and B.
A possible way to sort the samples is to find the position of the smallest one (P1) and reverse the order between positions 1 and P1, which causes the smallest sample to become first. Then we find the second one on position P and reverse the order between 2 and P2. Then the third sample is located etc.

The picture shows a simple example of 6 samples. The smallest one is on the 4th position, therefore, the robot arm reverses the first 4 samples. The second smallest sample is the last one, so the next robot operation will reverse the order of five samples on positions 2–6. The third step will be to reverse the samples 3–4, etc.
Your task is to find the correct sequence of reversal operations that will sort the samples using the above algorithm. If there are more samples with the same height, their mutual order must be preserved: the one that was given first in the initial order must be placed before the others in the final order too.
The last scenario is followed by a line containing zero.
Each Pi must be an integer (1 ≤ Pi ≤ N ) giving the position of the i-th sample just before the i-th reversal operation.
Note that if a sample is already on its correct position Pi , you should output the number Pi anyway, indicating that the “interval between Pi and Pi ” (a single sample) should be reversed.

/* * HDU 1890 Robotic Sort * 這題就是對 n個不同的數的排序過程,通過旋轉將第i到的數放在正確的位置,題目就是輸出每次旋轉前第i大的數的位置。 * 這題的意思就是直接按照結點編號1-n建立一顆樹。每個數代表初始位置的那個點,記錄下每個數對應的點。 * 每一次就是把第i大的數旋轉到根結點。刪除這個點然后旋轉左區間。 * 其實也可以用區間模擬,不刪除的做法。 * 操作有:反轉、刪除根結點 * 需要的變量:pre,ch,size,rev; */ #include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; const int MAXN=100010; int pre[MAXN],ch[MAXN][2],size[MAXN],rev[MAXN]; int root,tot1; int n; void NewNode(int &r,int father,int k) { r=k; pre[r]=father; ch[r][0]=ch[r][1]=0; size[r]=1; rev[r]=0; } //反轉的更新 void Update_Rev(int r) { if(!r)return; swap(ch[r][0],ch[r][1]); rev[r]^=1; } void Push_Up(int r) { size[r]=size[ch[r][0]]+size[ch[r][1]]+1; } void Push_Down(int r) { if(rev[r]) { Update_Rev(ch[r][0]); Update_Rev(ch[r][1]); rev[r]=0; } } void Build(int &x,int l,int r,int father) { if(l>r)return; int mid=(l+r)/2; NewNode(x,father,mid); Build(ch[x][0],l,mid-1,x); Build(ch[x][1],mid+1,r,x); Push_Up(x);//這個不用忘記 } void Init() { root=tot1=0; ch[root][0]=ch[root][1]=size[root]=rev[root]=0; Build(root,1,n,0); } //旋轉,基本固定 void Rotate(int x,int kind) { int y=pre[x]; Push_Down(y); Push_Down(x); ch[y][!kind]=ch[x][kind]; pre[ch[x][kind]]=y; if(pre[y]) ch[pre[y]][ch[pre[y]][1]==y]=x; pre[x]=pre[y]; ch[x][kind]=y; pre[y]=x; Push_Up(y); } //Splay調整 void Splay(int r,int goal) { Push_Down(r); while(pre[r]!=goal) { if(pre[pre[r]]==goal) { //這題有反轉操作,需要先push_down,在判斷左右孩子 Push_Down(pre[r]); Push_Down(r); Rotate(r,ch[pre[r]][0]==r); } else { //這題有反轉操作,需要先push_down,在判斷左右孩子 Push_Down(pre[pre[r]]); Push_Down(pre[r]); Push_Down(r); int y=pre[r]; int kind=(ch[pre[y]][0]==y); //兩個方向不同,則先左旋再右旋 if(ch[y][kind]==r) { Rotate(r,!kind); Rotate(r,kind); } //兩個方向相同,相同方向連續兩次 else { Rotate(y,kind); Rotate(r,kind); } } } Push_Up(r); if(goal==0)root=r; } int Get_Min(int r) { Push_Down(r); while(ch[r][0]) { r=ch[r][0]; Push_Down(r); } return r; } int Get_Max(int r) { Push_Down(r); while(ch[r][1]) { r=ch[r][1]; Push_Down(r); } return r; } //刪除根結點 void Remove() { if(ch[root][0]==0)//沒有左孩子 { root=ch[root][1]; pre[root]=0; } else { int m=Get_Max(ch[root][0]); Splay(m,root); ch[m][1]=ch[root][1]; pre[ch[root][1]]=m; root=m; pre[root]=0; Push_Up(root);//要更新 } } struct Node { int id,num; }a[MAXN]; bool cmp(Node n1,Node n2) { if(n1.num!=n2.num)return n1.num<n2.num; else return n1.id<n2.id; } int main() { while(scanf("%d",&n)==1&&n) { Init(); for(int i=1;i<=n;i++) { scanf("%d",&a[i].num); a[i].id=i; } sort(a+1,a+1+n,cmp); for(int i=1;i<n;i++) { Splay(a[i].id,0); Update_Rev(ch[root][0]); printf("%d ",i+size[ch[root][0]]); Remove(); } printf("%d\n",n); } return 0; }
4、POJ 3580 SuperMemo(成段更新、區間最小值、反轉、插入和刪除、區間搬移)
Time Limit: 5000MS | Memory Limit: 65536K | |
Total Submissions: 5839 | Accepted: 1884 | |
Case Time Limit: 2000MS |
Description
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
都是伸展樹比較經典的操作。
特別的是其中的循環右移操作。循環右移[l,r] T次,其實就是把區間[l,r-T]放在[r-T+1,r]后面。就是區間搬移。但是T必須先對長度取模

/* * 給定一個數列a1,a2,...an * 進行以下6種操作 * ADD x y D :給第x個數到第y個數加D(增加一個add進行延遲標記) * REVERSE x y :反轉[x,y]之間的數(伸展樹經典操作) * REVOLVE x y T:循環右移T次(先把T對長度進行取模,然后就相當於把[y-T+1,y]放在[x,y-T]前面) * INSERT x P:在第x個數后面插入P (經典的插入) * DELETE x:刪除第x個數(刪除操作) * MIN x y:查詢[x,y]之間最小的數(標記) * * 需要的操作:反轉、刪除、插入、查詢區間最小值、成段更新、區間搬移(循環右移轉化為區間搬移) * 需要的變量:pre,ch,key,size,add,rev,m(最小值) */ #include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define Key_value ch[ch[root][1]][0] #define Key_Value ch[ch[root][1]][0] const int MAXN=200010; const int INF=0x3f3f3f3f; int pre[MAXN],ch[MAXN][2],key[MAXN],size[MAXN],add[MAXN],rev[MAXN],m[MAXN]; int root,tot1; int s[MAXN],tot2;//內存池、內存池容量 int a[MAXN]; int n,q; void NewNode(int &r,int father,int k) { if(tot2)r=s[tot2--]; else r=++tot1; ch[r][0]=ch[r][1]=0; pre[r]=father; size[r]=1; add[r]=rev[r]=0; key[r]=m[r]=k; } void Update_Rev(int r) { if(!r)return; swap(ch[r][0],ch[r][1]); rev[r]^=1; } void Update_Add(int r,int ADD) { if(!r)return; add[r]+=ADD; key[r]+=ADD; m[r]+=ADD; } void Push_Up(int r) { size[r]=size[ch[r][0]]+size[ch[r][1]]+1; m[r]=key[r]; if(ch[r][0])m[r]=min(m[r],m[ch[r][0]]); if(ch[r][1])m[r]=min(m[r],m[ch[r][1]]); } void Push_Down(int r) { if(rev[r]) { Update_Rev(ch[r][0]); Update_Rev(ch[r][1]); rev[r]=0; } if(add[r]) { Update_Add(ch[r][0],add[r]); Update_Add(ch[r][1],add[r]); add[r]=0; } } void Build(int &x,int l,int r,int father) { if(l>r)return; int mid=(l+r)/2; NewNode(x,father,a[mid]); Build(ch[x][0],l,mid-1,x); Build(ch[x][1],mid+1,r,x); Push_Up(x); } void Init() { root=tot1=tot2=0; ch[root][0]=ch[root][1]=size[root]=add[root]=rev[root]=pre[root]=0; m[root]=INF;//這個不用也可以,如果在push_up那判斷了的話,否則需要 NewNode(root,0,INF); NewNode(ch[root][1],root,INF); Build(Key_value,1,n,ch[root][1]); Push_Up(ch[root][1]); Push_Up(root); } //旋轉 void Rotate(int x,int kind) { int y=pre[x]; Push_Down(y); Push_Down(x); ch[y][!kind]=ch[x][kind]; pre[ch[x][kind]]=y; if(pre[y]) ch[pre[y]][ch[pre[y]][1]==y]=x; pre[x]=pre[y]; ch[x][kind]=y; pre[y]=x; Push_Up(y); } //Splay調整 void Splay(int r,int goal) { Push_Down(r); while(pre[r]!=goal) { if(pre[pre[r]]==goal) { //這題有反轉操作,需要先push_down,在判斷左右孩子 Push_Down(pre[r]); Push_Down(r); Rotate(r,ch[pre[r]][0]==r); } else { //這題有反轉操作,需要先push_down,在判斷左右孩子 Push_Down(pre[pre[r]]); Push_Down(pre[r]); Push_Down(r); int y=pre[r]; int kind=(ch[pre[y]][0]==y); //兩個方向不同,則先左旋再右旋 if(ch[y][kind]==r) { Rotate(r,!kind); Rotate(r,kind); } //兩個方向相同,相同方向連續兩次 else { Rotate(y,kind); Rotate(r,kind); } } } Push_Up(r); if(goal==0)root=r; } int Get_Kth(int r,int k) { Push_Down(r); int t=size[ch[r][0]]+1; if(t==k)return r; if(t>k)return Get_Kth(ch[r][0],k); else return Get_Kth(ch[r][1],k-t); } int Get_Min(int r) { Push_Down(r); while(ch[r][0]) { r=ch[r][0]; Push_Down(r); } return r; } int Get_Max(int r) { Push_Down(r); while(ch[r][1]) { r=ch[r][1]; Push_Down(r); } return r; } //下面是操作了 void ADD(int l,int r,int D) { Splay(Get_Kth(root,l),0); Splay(Get_Kth(root,r+2),root); Update_Add(Key_value,D); Push_Up(ch[root][1]); Push_Up(root); } void Reverse(int l,int r) { Splay(Get_Kth(root,l),0); Splay(Get_Kth(root,r+2),root); Update_Rev(Key_value); Push_Up(ch[root][1]); Push_Up(root); } void Revolve(int l,int r,int T)//循環右移 { int len=r-l+1; T=(T%len+len)%len; if(T==0)return; int c=r-T+1;//將區間[c,r]放在[l,c-1]前面 Splay(Get_Kth(root,c),0); Splay(Get_Kth(root,r+2),root); int tmp=Key_value; Key_value=0; Push_Up(ch[root][1]); Push_Up(root); Splay(Get_Kth(root,l),0); Splay(Get_Kth(root,l+1),root); Key_value=tmp; pre[Key_value]=ch[root][1];//這個不用忘記 Push_Up(ch[root][1]); Push_Up(root); } void Insert(int x,int P)//在第x個數后面插入P { Splay(Get_Kth(root,x+1),0); Splay(Get_Kth(root,x+2),root); NewNode(Key_value,ch[root][1],P); Push_Up(ch[root][1]); Push_Up(root); } void erase(int r)//回收內存 { if(r) { s[++tot2]=r; erase(ch[r][0]); erase(ch[r][1]); } } void Delete(int x)//刪除第x個數 { Splay(Get_Kth(root,x),0); Splay(Get_Kth(root,x+2),root); erase(Key_value); pre[Key_value]=0; Key_value=0; Push_Up(ch[root][1]); Push_Up(root); } int Query_Min(int l,int r) { Splay(Get_Kth(root,l),0); Splay(Get_Kth(root,r+2),root); return m[Key_value]; } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); char op[20]; int x,y,z; while(scanf("%d",&n)==1) { for(int i=1;i<=n;i++)scanf("%d",&a[i]); Init(); scanf("%d",&q); while(q--) { scanf("%s",op); if(strcmp(op,"ADD")==0) { scanf("%d%d%d",&x,&y,&z); ADD(x,y,z); } else if(strcmp(op,"REVERSE")==0) { scanf("%d%d",&x,&y); Reverse(x,y); } else if(strcmp(op,"REVOLVE")==0) { scanf("%d%d%d",&x,&y,&z); Revolve(x,y,z); } else if(strcmp(op,"INSERT")==0) { scanf("%d%d",&x,&y); Insert(x,y); } else if(strcmp(op,"DELETE")==0) { scanf("%d",&x); Delete(x); } else { scanf("%d%d",&x,&y); printf("%d\n",Query_Min(x,y)); } } } return 0; }
5、文本編輯器editor (字符串的插入、刪除、反轉、查詢第k個字符)
Description
這些日子,可可不和卡卡一起玩了,原來可可正廢寢忘食的想做一個簡單而高效的文本編輯器。你能幫助他嗎?為了明確任務目標,可可對“文本編輯器”做了一個抽象的定義:
文本:由0個或多個字符構成的序列。這些字符的ASCII碼在閉區間[32, 126]內,也就是說,這些字符均為可見字符或空格。光標:在一段文本中用於指示位置的標記,可以位於文本的第一個字符之前,文本的最后一個字符之后或文本的某兩個相鄰字符之間。文本編輯器:為一個可以對一段文本和該文本中的一個光標進行如下七條操作的程序。如果這段文本為空,我們就說這個文本編輯器是空的。 編寫一個程序: 建立一個空的文本編輯器。 從輸入文件中讀入一些操作指令並執行。 對所有執行過的GET操作,將指定的內容寫入輸出文件。
Input
輸入文件中第一行是指令條數N,以下是需要執行的N個操作。除了回車符之外,輸入文件的所有字符的ASCII碼都在閉區間[32, 126]內。且行尾沒有空格。
Output
依次對應輸入文件中每條GET指令的輸出,不得有任何多余的字符。
Sample Input
10
Insert 13
Balanced eert
Move 2
Delete 5
Next
Insert 7
editor
Move 0
Get
Move 11
Rotate 4
Get
Sample Output
B
t
Hint
對輸入數據我們有如下假定: MOVE操作不超過50 000個,INSERT、DELETE和ROTATE操作作的總個數不超過6 000,GET操作不超過20 000個,PREV和NEXT操作的總個數不超過20 000。 所有INSERT插入的字符數之和不超過2M(1M=1 024*1 024)。 DELETE操作、ROTATE操作和GET操作執行時光標后必然有足夠的字符。MOVE、PREV、NEXT操作不會把光標移動到非法位置。 輸入文件沒有錯誤。
關於字符串的一些操作,都是伸展樹的基本的操作。

/* * 六種操作 * Move k : 將光標移到到第k個字符之后,k=0則移到到最前面 * Insert n * S :在光標后插入長度為n的字符串S * Delete n :刪除光標后的n個字符 * Rotate n:反轉光標后的n個字符 * Get:輸出光標后的一個字符,光標位置不變 * Prev : 光標前移一個字符 * Next : 光標后移一個字符 * 用一個變量記錄光標的位置,Move , Prev , Next直接改變光標變量即可 * Insert:插入操作,旋轉之后插入到根的右孩子的左子樹 * Delete : Delete操作,刪除右孩子的左子樹 * Rotate : 經典操作,區間反轉,通過一個延遲標記記錄 * Get : 得到光標位置的后繼,Get_Kth */ #include <iostream> #include <stdio.h> #include <string.h> #include <algorithm> using namespace std; #define Key_value ch[ch[root][1]][0] const int MAXN=2*1024*1024+5; int ch[MAXN][2],pre[MAXN],rev[MAXN],size[MAXN]; char key[MAXN]; int root,tot1; int s[MAXN],tot2; int pos;//光標位置 char str[MAXN];//要插入的字符串 void NewNode(int &r,int father,char k) { if(tot2)r=s[tot2--]; else r=++tot1; ch[r][0]=ch[r][1]=0; pre[r]=father; rev[r]=0; key[r]=k; size[r]=1; } void Update_Rev(int r) { if(!r)return; swap(ch[r][0],ch[r][1]); rev[r]^=1; } void Push_Up(int r) { size[r]=size[ch[r][0]]+size[ch[r][1]]+1; } void Push_Down(int r) { if(rev[r]) { Update_Rev(ch[r][0]); Update_Rev(ch[r][1]); rev[r]=0; } } void Build(int &x,int l,int r,int father) { if(l>r)return; int mid=(l+r)/2; NewNode(x,father,str[mid]); Build(ch[x][0],l,mid-1,x); Build(ch[x][1],mid+1,r,x); Push_Up(x); } void Init() { pos=0; root=tot1=tot2=0; ch[root][0]=ch[root][1]=pre[root]=size[root]=rev[root]=0; NewNode(root,0,' '); NewNode(ch[root][1],root,' '); Push_Up(ch[root][1]); Push_Up(root); } void Rotate(int x,int kind) { int y=pre[x]; Push_Down(y); Push_Down(x); ch[y][!kind]=ch[x][kind]; pre[ch[x][kind]]=y; if(pre[y]) ch[pre[y]][ch[pre[y]][1]==y]=x; pre[x]=pre[y]; ch[x][kind]=y; pre[y]=x; Push_Up(y); } void Splay(int r,int goal) { Push_Down(r); while(pre[r]!=goal) { if(pre[pre[r]]==goal) { Push_Down(pre[r]); Push_Down(r); Rotate(r,ch[pre[r]][0]==r); } else { Push_Down(pre[pre[r]]); Push_Down(pre[r]); Push_Down(r); int y=pre[r]; int kind=ch[pre[y]][0]==y;; if(ch[y][kind]==r) { Rotate(r,!kind); Rotate(r,kind); } else { Rotate(y,kind); Rotate(r,kind); } } } Push_Up(r); if(goal==0)root=r; } int Get_Kth(int r,int k) { Push_Down(r); int t=size[ch[r][0]]+1; if(t==k)return r; if(t>k)return Get_Kth(ch[r][0],k); else return Get_Kth(ch[r][1],k-t); } int Get_Min(int r) { Push_Down(r); while(ch[r][0]) { r=ch[r][0]; Push_Down(r); } return r; } int Get_Max(int r) { Push_Down(r); while(ch[r][1]) { r=ch[r][1]; Push_Down(r); } return r; } void INSERT(int len)//在光標后插入長度為len的字符串str { Splay(Get_Kth(root,pos+1),0); Splay(Get_Min(ch[root][1]),root); Build(Key_value,0,len-1,ch[root][1]); Push_Up(ch[root][1]); Push_Up(root); } void erase(int r) { if(r) { s[++tot2]=r; erase(ch[r][0]); erase(ch[r][1]); } } void DELETE(int len)//刪除光標后len個 { Splay(Get_Kth(root,pos+1),0); Splay(Get_Kth(root,pos+len+2),root); erase(Key_value); pre[Key_value]=0; Key_value=0; Push_Up(ch[root][1]); Push_Up(root); } void Reverse(int len)//反轉光標后len個字符 { Splay(Get_Kth(root,pos+1),0); Splay(Get_Kth(root,pos+len+2),root); Update_Rev(Key_value); Push_Up(ch[root][1]); Push_Up(root); } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int n; int x; char op[20]; while(scanf("%d",&n)==1) { Init(); while(n--) { scanf("%s",op); if(op[0]=='I') { scanf("%d%*c",&x); gets(str); INSERT(x); } else if(op[0]=='M') { scanf("%d",&x); pos=x; } else if(op[0]=='D') { scanf("%d",&x); DELETE(x); } else if(op[0]=='R') { scanf("%d",&x); Reverse(x); } else if(op[0]=='G') { printf("%c\n",key[Get_Kth(root,pos+2)]); } else if(op[0]=='P')pos--; else pos++; } } return 0; }
6、HDU 3487 Play with Chain(區間切割、區間反轉)
Play with Chain
Time Limit: 6000/2000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 2425 Accepted Submission(s): 978
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?
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.

/* * 初始序列是1,2,3,...,n * 進行兩種操作,區間切割和區間反轉 * CUT a b c :把[a,b]切割下來,然后放在第c個元素后面 * FLIP a b :反轉區間[a,b] */ #include <iostream> #include <stdio.h> #include <string.h> #include <iostream> using namespace std; #define Key_value ch[ch[root][1]][0] const int MAXN=300010; int pre[MAXN],ch[MAXN][2],key[MAXN],size[MAXN]; int rev[MAXN]; int root,tot1; int n,q; //debug void Treavel(int x) { if(x) { Treavel(ch[x][0]); printf("結點 %2d:左兒子 %2d 右兒子 %2d 父結點 %2d key=%2d,size=%2d,rev=%2d\n",x,ch[x][0],ch[x][1],pre[x],key[x],size[x],rev[x]); Treavel(ch[x][1]); } } void debug() { printf("root:%d\n",root); Treavel(root); } void NewNode(int &r,int father,int k) { r=++tot1; ch[r][0]=ch[r][1]=0; size[r]=1; key[r]=k; rev[r]=0; pre[r]=father; } void Update_Rev(int r) { if(!r)return; swap(ch[r][0],ch[r][1]); rev[r]^=1; } void Push_Up(int r) { size[r]=size[ch[r][0]]+size[ch[r][1]]+1; } void Push_Down(int r) { if(rev[r]) { Update_Rev(ch[r][0]); Update_Rev(ch[r][1]); rev[r]=0; } } void Build(int &x,int l,int r,int father) { if(l>r)return; int mid=(l+r)/2; NewNode(x,father,mid); Build(ch[x][0],l,mid-1,x); Build(ch[x][1],mid+1,r,x); Push_Up(x); } void Init() { root=tot1=0; ch[root][0]=ch[root][1]=size[root]=key[root]=pre[root]=rev[root]=0; NewNode(root,0,-1); NewNode(ch[root][1],root,-1); Build(Key_value,1,n,ch[root][1]); Push_Up(ch[root][1]); Push_Up(root); } void Rotate(int x,int kind) { int y=pre[x]; Push_Down(y); Push_Down(x); ch[y][!kind]=ch[x][kind]; pre[ch[x][kind]]=y; if(pre[y]) ch[pre[y]][ch[pre[y]][1]==y]=x; pre[x]=pre[y]; ch[x][kind]=y; pre[y]=x; Push_Up(y); } void Splay(int r,int goal) { Push_Down(r); while(pre[r]!=goal) { if(pre[pre[r]]==goal) { Push_Down(pre[r]); Push_Down(r); Rotate(r,ch[pre[r]][0]==r); } else { Push_Down(pre[pre[r]]); Push_Down(pre[r]); Push_Down(r); int y=pre[r]; int kind=ch[pre[y]][0]==y; if(ch[y][kind]==r) { Rotate(r,!kind); Rotate(r,kind); } else { Rotate(y,kind); Rotate(r,kind); } } } Push_Up(r); if(goal==0)root=r; } int Get_Kth(int r,int k) { Push_Down(r); int t=size[ch[r][0]]+1; if(t==k)return r; if(t>k)return Get_Kth(ch[r][0],k); else return Get_Kth(ch[r][1],k-t); } void CUT(int l,int r,int c) { Splay(Get_Kth(root,l),0); Splay(Get_Kth(root,r+2),root); int tmp=Key_value; Key_value=0; Push_Up(ch[root][1]); Push_Up(root); Splay(Get_Kth(root,c+1),0); Splay(Get_Kth(root,c+2),root); Key_value=tmp; pre[Key_value]=ch[root][1]; Push_Up(ch[root][1]); Push_Up(root); } void Reverse(int l,int r) { Splay(Get_Kth(root,l),0); Splay(Get_Kth(root,r+2),root); Update_Rev(Key_value); Push_Up(ch[root][1]); Push_Up(root); } int cnt; void Inorder(int r) { if(!r)return; Push_Down(r); Inorder(ch[r][0]); if(cnt>=1&&cnt<=n) { printf("%d",key[r]); if(cnt<n)printf(" "); else printf("\n"); } cnt++; Inorder(ch[r][1]); } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); char op[20]; int x,y,z; while(scanf("%d%d",&n,&q)==2) { if(n<0 && q<0)break; Init(); while(q--) { scanf("%s",op); if(op[0]=='C') { scanf("%d%d%d",&x,&y,&z); CUT(x,y,z); } else { scanf("%d%d",&x,&y); Reverse(x,y); } } cnt=0; Inorder(root); } return 0; }
7、HDU 3436 Queue-jumpers(離散化縮點)
這題主要是離散化,把線段縮成點,其余都是普通的操作了。
Queue-jumpers
Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1378 Accepted Submission(s): 329
1. Top x :Take person x to the front of the queue
2. Query x: calculate the current position of person x
3. Rank x: calculate the current person at position x
Where x is in [1, N].
Ponyo is so clever that she plays the game very well while Garfield has no idea. Garfield is now turning to you for help.
In each case, the first line contains two integers N(1<=N<=10^8), Q(1<=Q<=10^5). Then there are Q lines, each line contain an operation as said above.

/* * 對[1,N]排列的數進行以下三種操作 * 1:Top x 將數x拿到最前面 * 2:Query x 查詢x現在所在的位置 * 3:Rank x 查詢現在排在第x個數的位置 * N<=10^8 Q<=10^5 必須進行離散化。 * 將Query 和Top的數單獨出來,中間的其它區間縮點,保存長度即可。 * 縮點后的區間內部是有序的,而且不會改變,只要知道起點和長度就可以了,對於Rank可以得出來 */ #include <iostream> #include <string.h> #include <stdio.h> #include <algorithm> using namespace std; #define Key_value ch[ch[root][1]][0] const int MAXN=200010; int pre[MAXN],ch[MAXN][2],size[MAXN],num[MAXN]; int s[MAXN],e[MAXN]; int root,tot1; char op[MAXN][10]; int qnum[MAXN]; int p[MAXN]; int cnt; //debug void Treavel(int x) { if(x) { Treavel(ch[x][0]); printf("結點 %2d:左孩子 %2d 右孩子 %2d 父結點 %2d size=%2d,num=%2d,s=%2d,e=%2d\n",x,ch[x][0],ch[x][1],pre[x],size[x],num[x],s[x],e[x]); Treavel(ch[x][1]); } } void debug() { printf("root:%d\n",root); Treavel(root); } //以上debug void NewNode(int &r,int father,int k) { r=k; ch[r][0]=ch[r][1]=0; size[r]=num[r]=e[k]-s[k]+1; pre[r]=father; } void Push_Up(int r) { size[r]=size[ch[r][0]]+size[ch[r][1]]+num[r]; } void Build(int &x,int l,int r,int father) { if(l>r)return; int mid=(l+r)/2; NewNode(x,father,mid); Build(ch[x][0],l,mid-1,x); Build(ch[x][1],mid+1,r,x); Push_Up(x); } void Init() { root=0; ch[root][0]=ch[root][1]=num[root]=size[root]=pre[root]=0; Build(root,1,cnt,0); Push_Up(root); } void Rotate(int x,int kind) { int y=pre[x]; ch[y][!kind]=ch[x][kind]; pre[ch[x][kind]]=y; if(pre[y]) ch[pre[y]][ch[pre[y]][1]==y]=x; pre[x]=pre[y]; ch[x][kind]=y; pre[y]=x; Push_Up(y); } void Splay(int r,int goal) { while(pre[r]!=goal) { if(pre[pre[r]]==goal) Rotate(r,ch[pre[r]][0]==r); else { int y=pre[r]; int kind=ch[pre[y]][0]==y; if(ch[y][kind]==r) { Rotate(r,!kind); Rotate(r,kind); } else { Rotate(y,kind); Rotate(r,kind); } } } Push_Up(r); if(goal==0)root=r; } int Get_Min(int r) { while(ch[r][0]) { r=ch[r][0]; } return r; } void Delete()//刪掉根結點 { if(ch[root][1]==0 || ch[root][0]==0) { root=ch[root][0]+ch[root][1]; pre[root]=0; return; } int k=Get_Min(ch[root][1]);//找到右子樹中最小的 Splay(k,root);//旋轉過來,使得右子樹沒有左孩子 ch[ch[root][1]][0]=ch[root][0]; root=ch[root][1]; pre[ch[root][0]]=root; pre[root]=0; Push_Up(root); } int Bin(int x)//二分查找 { int l=1,r=cnt; while(l<=r) { int mid=(l+r)/2; if(s[mid]<=x&&x<=e[mid])return mid; if(x<s[mid])r=mid-1; else l=mid+1; } return -1; } void Top(int x) { int y=Bin(x); Splay(y,0); Delete(); Splay(Get_Min(root),0); ch[y][0]=0; ch[y][1]=root; pre[root]=y; root=y; pre[root]=0; Push_Up(root); } int Query(int x) { int y=Bin(x); Splay(y,0); return size[ch[root][0]]+1; } int Get_Rank(int r,int k) { int t=size[ch[r][0]]; if(k<=t)return Get_Rank(ch[r][0],k); else if(k<=t+num[r]) return s[r]+k-t-1; else return Get_Rank(ch[r][1],k-t-num[r]); } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int T; int n,q; int iCase=0; scanf("%d",&T); while(T--) { iCase++; printf("Case %d:\n",iCase); scanf("%d%d",&n,&q); int t=0; for(int i=0;i<q;i++) { scanf("%s%d",op[i],&qnum[i]); if(op[i][0]=='T'||op[i][0]=='Q') p[t++]=qnum[i]; } p[t++]=1; p[t++]=n; sort(p,p+t); t=unique(p,p+t)-p; cnt=0; for(int i=0;i<t;i++) { if(i>0 && p[i]-p[i-1]>1) { cnt++; s[cnt]=p[i-1]+1; e[cnt]=p[i]-1; } cnt++; s[cnt]=p[i]; e[cnt]=p[i]; } Init(); for(int i=0;i<q;i++) { if(op[i][0]=='T')Top(qnum[i]); else if(op[i][0]=='Q')printf("%d\n",Query(qnum[i])); else printf("%d\n",Get_Rank(root,qnum[i])); } } return 0; }
8、維修數列(經典題,插入、刪除、修改、翻轉、求和、求和最大的子序列)
Description

Input
Output
Sample Input
9 8
2 -6 3 5 1 -5 -3 6 3
GET-SUM 5 4
MAX-SUM
INSERT 8 3 -5 7 2
DELETE 12 1
MAKE-SAME 3 3 2
REVERSE 3 6
GET-SUM 5 4
MAX-SUM
Sample Output
-1
10
1
10

/* * 維修數列 * 經典的伸展樹的題目。 * 題目首先給出一個數列,然后進行下列6種操作 * 1:INSERT post tot c1,c2,...ctot :在當前數列的第pos個數字后插入tot個數字 * 2:DELETE pos tot : 從當前數列的第pos個數字開始連續 刪除tot個數字 * 3:MAKE-SAME pos tot c :將當前數列的第pos個數字開始連續的tot個數字統一修改為c * 4:REVERSE pos tot : 翻轉當前數列的第pos個數字來說的連續的tot個數字 * 5:GET-SUM pos tot :計算當前數列的第pos個數字來說的連續的tot個數字的和並輸出 * 6:MAX-SUM :求出當前數列中和最大的一段序列,輸出最大和 */ #include <iostream> #include <string.h> #include <stdio.h> #include <algorithm> using namespace std; #define Key_value ch[ch[root][1]][0] const int MAXN=500010; const int INF=0x3f3f3f3f; int pre[MAXN],ch[MAXN][2],key[MAXN],size[MAXN]; int sum[MAXN],rev[MAXN],same[MAXN]; int lx[MAXN],rx[MAXN],mx[MAXN]; int root,tot1; int s[MAXN],tot2; int a[MAXN]; int n,q; //debug部分 void Treavel(int x) { if(x) { Treavel(ch[x][0]); printf("結點%2d:左兒子 %2d 右兒子 %2d 父結點 %2d key=%2d, size= %2d, sum=%2d,rev=%2d same=%2d lx=%2d rx=%2d mx=%2d\n",x,ch[x][0],ch[x][1],pre[x],key[x],size[x],sum[x],rev[x],same[x],lx[x],rx[x],mx[x]); Treavel(ch[x][1]); } } void debug() { printf("root%d\n",root); Treavel(root); } void NewNode(int &r,int father,int k) { if(tot2)r=s[tot2--]; else r=++tot1; pre[r]=father; ch[r][0]=ch[r][1]=0; key[r]=k; sum[r]=k; rev[r]=same[r]=0; lx[r]=rx[r]=mx[r]=k; size[r]=1; } void Update_Same(int r,int v) { if(!r)return; key[r]=v; sum[r]=v*size[r]; lx[r]=rx[r]=mx[r]=max(v,v*size[r]); same[r]=1; } void Update_Rev(int r) { if(!r)return; swap(ch[r][0],ch[r][1]); swap(lx[r],rx[r]); rev[r]^=1;//這里要注意,一定是異或1 } void Push_Up(int r) { int lson=ch[r][0],rson=ch[r][1]; size[r]=size[lson]+size[rson]+1; sum[r]=sum[lson]+sum[rson]+key[r]; lx[r]=max(lx[lson],sum[lson]+key[r]+max(0,lx[rson])); rx[r]=max(rx[rson],sum[rson]+key[r]+max(0,rx[lson])); mx[r]=max(0,rx[lson])+key[r]+max(0,lx[rson]); mx[r]=max(mx[r],max(mx[lson],mx[rson])); } void Push_Down(int r) { if(same[r]) { Update_Same(ch[r][0],key[r]); Update_Same(ch[r][1],key[r]); same[r]=0; } if(rev[r]) { Update_Rev(ch[r][0]); Update_Rev(ch[r][1]); rev[r]=0; } } void Build(int &x,int l,int r,int father) { if(l>r)return; int mid=(l+r)/2; NewNode(x,father,a[mid]); Build(ch[x][0],l,mid-1,x); Build(ch[x][1],mid+1,r,x); Push_Up(x); } void Init() { root=tot1=tot2=0; ch[root][0]=ch[root][1]=pre[root]=size[root]=same[root]=rev[root]=sum[root]=key[root]=0; lx[root]=rx[root]=mx[root]=-INF; NewNode(root,0,-1); NewNode(ch[root][1],root,-1); for(int i=0;i<n;i++)scanf("%d",&a[i]); Build(Key_value,0,n-1,ch[root][1]); Push_Up(ch[root][1]); Push_Up(root); } void Rotate(int x,int kind) { int y=pre[x]; Push_Down(y); Push_Down(x); ch[y][!kind]=ch[x][kind]; pre[ch[x][kind]]=y; if(pre[y]) ch[pre[y]][ch[pre[y]][1]==y]=x; pre[x]=pre[y]; ch[x][kind]=y; pre[y]=x; Push_Up(y); } void Splay(int r,int goal) { Push_Down(r); while(pre[r]!=goal) { if(pre[pre[r]]==goal) { Push_Down(pre[r]); Push_Down(r); Rotate(r,ch[pre[r]][0]==r); } else { Push_Down(pre[pre[r]]); Push_Down(pre[r]); Push_Down(r); int y=pre[r]; int kind=ch[pre[y]][0]==y; if(ch[y][kind]==r) { Rotate(r,!kind); Rotate(r,kind); } else { Rotate(y,kind); Rotate(r,kind); } } } Push_Up(r); if(goal==0)root=r; } int Get_Kth(int r,int k) { Push_Down(r); int t=size[ch[r][0]]+1; if(t==k)return r; if(t>k)return Get_Kth(ch[r][0],k); else return Get_Kth(ch[r][1],k-t); } //在第pos個數后插入tot個數 void Insert(int pos,int tot) { for(int i=0;i<tot;i++)scanf("%d",&a[i]); Splay(Get_Kth(root,pos+1),0); Splay(Get_Kth(root,pos+2),root); Build(Key_value,0,tot-1,ch[root][1]); Push_Up(ch[root][1]); Push_Up(root); } void erase(int r) { if(!r)return; s[++tot2]=r; erase(ch[r][0]); erase(ch[r][1]); } //從第pos個數開始連續刪除tot個數 void Delete(int pos,int tot) { Splay(Get_Kth(root,pos),0); Splay(Get_Kth(root,pos+tot+1),root); erase(Key_value); pre[Key_value]=0; Key_value=0; Push_Up(ch[root][1]); Push_Up(root); } //從第pos個數連續開始的tot個數修改為c void Make_Same(int pos,int tot,int c) { Splay(Get_Kth(root,pos),0); Splay(Get_Kth(root,pos+tot+1),root); Update_Same(Key_value,c); Push_Up(ch[root][1]); Push_Up(root); } //反轉 void Reverse(int pos,int tot) { Splay(Get_Kth(root,pos),0); Splay(Get_Kth(root,pos+tot+1),root); Update_Rev(Key_value); Push_Up(ch[root][1]); Push_Up(root); } //求和 int Get_Sum(int pos,int tot) { Splay(Get_Kth(root,pos),0); Splay(Get_Kth(root,pos+tot+1),root); return sum[Key_value]; } //得到最大和 int Get_MaxSum(int pos,int tot) { Splay(Get_Kth(root,pos),0); Splay(Get_Kth(root,pos+tot+1),root); return mx[Key_value]; } void Inorder(int r) { if(!r)return; Push_Down(r); Inorder(ch[r][0]); printf("%d ",key[r]); Inorder(ch[r][1]); } int main() { //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); while(scanf("%d%d",&n,&q)==2) { Init(); char op[20]; int x,y,z; while(q--) { scanf("%s",op); if(op[0]=='I') { scanf("%d%d",&x,&y); Insert(x,y); } else if(op[0]=='D') { scanf("%d%d",&x,&y); Delete(x,y); } else if(op[0]=='M'&&op[2]=='K') { scanf("%d%d%d",&x,&y,&z); Make_Same(x,y,z); } else if(op[0]=='R') { scanf("%d%d",&x,&y); Reverse(x,y); } else if(op[0]=='G') { scanf("%d%d",&x,&y); printf("%d\n",Get_Sum(x,y)); } else { printf("%d\n",Get_MaxSum(1,size[root]-2)); } } } return 0; }