伸展树是比较神奇的,它可以做很多线段树不能实现的事情。
最近做伸展树做了好长时间了,现在重新把题目整理下,代码统一些一下呢。说明多是含在代码的注释中。
刚开始学,可以看论文,然后按照别人的代码去写。
我是参照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, A 1 , A 2 , ... , 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 A 1 , A 2 , ... , 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
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
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, {A 1 , A 2 , ... 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必须先对长度取模
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: 10000 MS Memory Limit: 165888 KB 64bit IO Format: %lld & %llu
Appoint description:
System Crawler (2013-04-08)
Description
这些日子,可可不和卡卡一起玩了,原来可可正废寝忘食的想做一个简单而高效的文本编辑器。你能帮助他吗?为了明确任务目标,可可对“文本编辑器”做了一个抽象的定义: 文本:由0个或多个字符构成的序列。这些字符的ASCII码在闭区间[32, 126]内,也就是说,这些字符均为可见字符或空格。光标:在一段文本中用于指示位置的标记,可以位于文本的第一个字符之前,文本的最后一个字符之后或文本的某两个相邻字符之间。文本编辑器:为一个可以对一段文本和该文本中的一个光标进行如下七条操作的程序。如果这段文本为空,我们就说这个文本编辑器是空的。 编写一个程序: 建立一个空的文本编辑器。 从输入文件中读入一些操作指令并执行。 对所有执行过的GET操作,将指定的内容写入输出文件。
Output
依次对应输入文件中每条GET指令的输出,不得有任何多余的字符。
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
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: 10000 MS Memory Limit: 65536 KB 64bit IO Format: %lld & %llu
Appoint description:
System Crawler (2012-06-23)
Output
对于输入数据中的GET-SUM和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 ;
}