Splay Tree(伸展樹總結)


伸展樹是比較神奇的,它可以做很多線段樹不能實現的事情。

最近做伸展樹做了好長時間了,現在重新把題目整理下,代碼統一些一下呢。說明多是含在代碼的注釋中。

剛開始學,可以看論文,然后按照別人的代碼去寫。

我是參照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 (成段更新、區間求和)

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, A1A2, ... , 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 A1A2, ... , AN. -1000000000 ≤ Ai ≤ 1000000000.
Each of the next Q lines represents an operation.
"C a b c" means adding c to each of AaAa+1, ... , Ab. -10000 ≤ c ≤ 10000.
"Q a b" means querying the sum of AaAa+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

The sums may exceed the range of 32-bit integers.

Source

 
View Code
/*
 * 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

 

這道題目很其他的比起來太簡單了。。。

隨便寫、、

 

 

View Code
/*
 * 這題的意思就是每插入一個數,累加和前面插入的數的差值。第一個數加本身。
 * 用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


Problem Description
Somewhere deep in the Czech Technical University buildings, there are laboratories for examining mechanical and electrical properties of various materials. In one of yesterday’s presentations, you have seen how was one of the laboratories changed into a new multimedia lab. But there are still others, serving to their original purposes. 

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.
 


Input
The input consists of several scenarios. Each scenario is described by two lines. The first line contains one integer number N , the number of samples, 1 ≤ N ≤ 100 000. The second line lists exactly N space-separated positive integers, they specify the heights of individual samples and their initial order. 

The last scenario is followed by a line containing zero.
 


Output
For each scenario, output one line with exactly N integers P1 , P1 , . . . PN ,separated by a space.
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. 
 


Sample Input
6 3 4 5 1 6 2 4 3 3 2 1 0
 


Sample Output
4 6 4 5 6 6 4 2 4 4
 


Source
 


Recommend
linle
 
 
 
 
主要是想法比較好,反正和刪除是很典型的操作了。
反轉的splay時候一定要先push_down下去,再判斷。
View Code
/*
 * 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(成段更新、區間最小值、反轉、插入和刪除、區間搬移)

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, {A1A2, ... An}. Then the host performs a series of operations and queries on the sequence which consists:

  1. 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}
  2. 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}
  3. REVOLVE x y T: rotate sub-sequence {Ax ... AyT times. For example, performing "REVOLVE 2 4 2" on {1, 2, 3, 4, 5} results in {1, 3, 4, 2, 5}
  4. 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}
  5. DELETE x: delete Ax. For example, performing "DELETE 2" on {1, 2, 3, 4, 5} results in {1, 3, 4, 5}
  6. 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 (≤ 100000).

The following n lines describe the sequence.

Then follows 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必須先對長度取模



View Code
/*
 * 給定一個數列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個字符)

文本編輯器editor
Time Limit:10000MS     Memory Limit:165888KB     64bit IO Format:%lld & %llu
Submit  Status
Appoint description: 

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操作不會把光標移動到非法位置。 輸入文件沒有錯誤。






關於字符串的一些操作,都是伸展樹的基本的操作。

View Code
/*
 * 六種操作
 * 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


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
 
 
        
Source
 
 
        
Recommend
zhengfeng
 
 
 
算是比較簡單的伸展樹了
只有兩種操作。
 
View Code
/*
 * 初始序列是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


Problem Description
Ponyo and Garfield are waiting outside the box-office for their favorite movie. Because queuing is so boring, that they want to play a game to kill the time. The game is called “Queue-jumpers”. Suppose that there are N people numbered from 1 to N stand in a line initially. Each time you should simulate one of the following operations:
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.
 

 

Input
In the first line there is an integer T, indicates the number of test cases.(T<=50)
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. 
 

 

Output
For each test case, output “Case d:“ at first line where d is the case number counted from one, then for each “Query x” operation ,output the current position of person x at a line, for each “Rank x” operation, output the current person at position x at a line.
 

 

Sample Input
3 9 5 Top 1 Rank 3 Top 7 Rank 6 Rank 8 6 2 Top 4 Top 5 7 4 Top 5 Top 2 Query 1 Rank 6
 

 

Sample Output
Case 1: 3 5 8 Case 2: Case 3: 3 6
 

 

Author
wzc1989
 

 

Source
 

 

Recommend
zhouzeyong
 
View Code
/*
 * 對[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、維修數列(經典題,插入、刪除、修改、翻轉、求和、求和最大的子序列)

維修數列
Time Limit:10000MS     Memory Limit:65536KB     64bit IO Format:%lld & %llu
Submit  Status
Appoint description: 

Description

Input

輸入文件的第1行包含兩個數N和M,N表示初始時數列中數的個數,M表示要進行的操作數目。 第2行包含N個數字,描述初始時的數列。 以下M行,每行一條命令,格式參見問題描述中的表格。

Output

對於輸入數據中的GET-SUM和MAX-SUM操作,向輸出文件依次打印結果,每個答案(數字)占一行。

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


View Code
/*
 * 維修數列
 * 經典的伸展樹的題目。
 * 題目首先給出一個數列,然后進行下列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;
}

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM