splay树对于区间操作比线段树更容易操作,编写代码也更容易,调试起来也很方便。以下我大概说说splay的几个操作以及我对几个题目的分析。
首先是资料,请点击 here 下载(如果不行的话,麻烦跟我说一下,谢谢)
以下我们先定义几个数组和变量:
// lx表示x的左儿子,rx表示x的右儿子,px表示x的父节点,root表示根节点,其他的类似 #define lx ch[x][0] #define rx ch[x][1] #define px pre[x] #define ly ch[y][0] #define ry ch[y][1] #define py pre[y] #define lz ch[z][0] #define rz ch[z][1] #define pz pre[z] #define rt ch[root][1] #define lrt ch[rt][0] // ch数组表示x的左右儿子,pre表示x的父节点,sz表示大小,rev表示区间翻转标记,str表示插入的字符串 int ch[X][2],pre[X],sz[X],rev[X]; char str[X];
首先,对于初始化建树的问题。我们首先需要建立两个或者以上的额外节点,建立的过程如下图:
(节点1,2都是一个额外的节点(跟题目没有半毛钱关系。。。))
比如我们需要用splay维护字符串abcdefgh:
1.建树,如上
2.在 根的右儿子的左儿子 中插入
3.为了使得树的深度不太深,我们需要递归把字符串加在 根的右儿子的左儿子 上。建完树之后应该如下:(可以假设两个虚拟节点1,2的字符为未出现在题目中的字符)

该代码如下:
// 初始化建树的问题
inline void new_node(int &x,int y,int c){ // 注意x需要在这修改,修改之后的值需要传回上一个函数
x = ++tot;
ha[x] = val[x] = c;
px = y;
lx = rx = 0;
}
inline void build(int &x,int y,int l,int r){ // 对于区间[l,r]的插入
if(l>r) return;
int mid = (l+r)>>1;
new_node(x,y,str[mid]-'a'+1); // 其他的题目不一定是str数组了
build(lx,x,l,mid-1);
build(rx,x,mid+1,r);
update(x);
}
inline void init(){
// 初始化,如果只用一次的话,我们定义的是全局变量,所以不用置为0,如果需要的话,我们也可以直接把根的所有有关数组置为0就行了,无需把全部清零
//memset(ch,0,sizeof(ch));
//memset(sz,0,sizeof(sz));
//memset(pre,0,sizeof(pre));
root = tot = 0;
new_node(root,0,0);
new_node(rt,root,0);
update(rt); // 需要更新一下新插入的节点,具体看题目而定
update(root);
gets(str);
int n = strlen(str);
build(lrt,rt,0,n-1);
update(rt);
update(root);
}
update函数一般的写法如下:
inline void update(int x){
sz[x] = sz[lx]+sz[rx]+1;
// 如果需要维护一下sum,lsum,rsum或者hash的话,在这里添加相应代码
}
push_down函数看具体的题目而定,主要是及时把lazy标记下沉
然后是splay和旋转操作,由于给出的资料或者其它的一些资料已经很详细了,这里不细说。splay(x,goal)操作就是把x旋转到x的父节点为goal的操作,他是自底向上的操作。在操作过程中,zag,zig其实就相当于普通的左旋、右旋。以下为代码:
inline int sgn(int x){ // 判断x是y的左或者右儿子,0左,1右
return ch[px][1]==x;
}
inline void setc(int y,int d,int x){ // 把x置为y的儿子
ch[y][d] = x;
px = y;
}
inline void rot(int x,int d){ // 旋转操作,自己动手画画就好了
int y = px;
int z = py;
//push_down(y);
//push_down(x);
setc(y,!d,ch[x][d]);
if(z) setc(z,sgn(y),x);
px = z;
setc(x,d,y);
update(y);
}
inline void splay(int x,int goal=0){ // splay的关键操作
if(!x)
return;
//push_down(x);
while(px!=goal){
int y = px;
int z = py;
if(z==goal){
rot(x,!sgn(x));
break;
}
if(lz==y){
ly==x?rot(y,1):rot(x,0);
rot(x,1);
}
else{
ry==x?rot(y,0):rot(x,1);
rot(x,0);
}
}
update(x);
if(goal==0)
root = x;
}
下面我们主要说说splay具体能够做什么
首先,我们需要知道第k小、得到以x为根的最小值元素,直接贴代码了。。
inline int get_Kth(int x,int k){
//push_down(x); // 是否需要把标记下沉
int tmp = sz[lx]+1;
if(tmp==k)
return x;
if(k<tmp)
return get_Kth(lx,k);
return get_Kth(rx,k-tmp);
}
inline int get_min(int x){
//push_down(x);
while(lx){
x = lx;
//push_down(x);
}
return x;
}
为了方便描述,rt表示根的右儿子(right_root), lrt表示根的右儿子的左儿子(left_right_root)
1.插入操作:在位置k上插入一个元素。
我们首先需要得到第k+1小的元素下标x(这里的小指的是树中按照sz大小来决定),然后把x旋转至根,旋转完了之后,我们需要得到x的后继y,把y旋转至根的右儿子。这时,我们发现根的右儿子的左子树为空。然后我们在lrt中新建一个节点就行了。
为什么找的不是第k小的元素呢?还记得我们为了代码更加简单,构造的两个虚拟节点吗?刚好实在插入的所有元素的两端,并且插入的时候实际上是该元素的后面,所以我们找的是第k+1小的元素。
比如我们在@abcdefgh@的树中,在第4位后面插入新节点q,我们插入的位置是d,e的中间,即第5位和5的后继
inline void Insert(){ // 插入操作
int x,k;
char s[2];
scanf("%d%s",&k,s);
x = get_Kth(root,k+1); // 为什么不是k?
splay(x);
int y = get_Min(rt); // 得到x的后继
splay(y,root);
new_node(lrt,rt,s[0]-'a'+1); // 新建一个节点
update(lrt); //及时更新插入之后的信息!
update(rt);
update(root);
}
2.插入操作二:成段插入。
假设我们需要插入的位置为pos,然后插入n个元素,那么插入的操作的过程是:首先寻找pos+1的元素x,然后找到他的后继,再把n个元素通过build递归建在lrt上。
inline void Insert(){
int pos,n;
scanf("%d%d",&pos,&n);
rep(i,k)
cin>>str[i];
pos ++;
int x = get_Kth(root,pos);
splay(x);
int y = get_min(rt);
splay(y,root);
build(lrt,rt,0,n-1);
//update(lrt); 可以不需要,在build函数中已经更新
update(rt);
update(root);
}
3.删除操作:删除一个区间(一个节点其实也是小区间)
假设我们需要删除区间[a,b],我们需要找到a的前驱以及b的后继,使得以lrt为根子树恰好为区间[a,b]。首先,我们需要找到第a小元素x,然后再把x splay到根,然后再找到b的后继,即找到第b+2小的元素y,把y splay到根的右儿子,这时,我们发现区间[a,b]的元素实际上就是以lrt为根的子树的所有元素。
为什么这个时候是a而不是a+1呢?因为第a小才是实际上的a的前驱,而我们执行完上述的操作之后,lrt才是真正的区间[a,b]。这个时候根为区间[a,b]的前一个元素,b+2为后一个元素。
然后我们把lrt的信息以及rt的信息修改一下就行了,最后需要update(rt),update(root);及时更新一下rt和root
以下为代码:
inline void Delete(){ // 删除区间 [a,b]
int a,b;
scanf("%d%d",&a,&b);
int x = get_Kth(root,a);
int y = get_Kth(root,b+2);
splay(x);
splay(y,root);
//del(lrt); 建立内存池,人工回收节点编号(在删除操作太频繁的时候,可能节点的编号使得数组太大,内存不足)
pre[lrt] = 0;
lrt = 0;
update(rt);
update(root);
}
4.翻转操作:把区间[a,b]翻转
我们用一个rev[]数组进行类似于线段树的lazy标记。
同样我们先把 第a小 旋转到到根,第b+2小 旋转到rt,区间[a,b]即为lrt。这时,我们对于节点lrt的rev标记置反就好了。注意这个时候向不向上更新根据具体的题目而定,一般是不需要的。代码:
inline void Rotate(){
int a,b;
scanf("%d%d",&a,&b);
int x = get_Kth(root,a);
splay(x);
int y = get_Kth(root,b+2);
splay(y,root);
rev[lrt] ^= 1;
}
注意:由于我们有了翻转标记,所以在标记下沉的时候,如果有标记的话,需要把左右子树交换一下。同时,如果有其他的数据维护,考虑一下需不需要交换。。。具体的看 S级别的NOI2005维修数列 的splay操作,做完之后,基本上的区间操作都会做了。
5.区间赋值:把区间[a,b]的所有元素置为c
其实我们发现这个跟上面翻转操作基本一样。所以我直接上代码了。。。
inline void Make_same(){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
int x = get_Kth(root,a);
int y = get_Kth(root,b+2);
splay(x);
splay(y,root);
update_same(lrt,c);
update(rt);
update(root);
}
注意:有标记,考虑下沉时候的操作(需要维护sum,lmax,rmax,val等等...)。
6.区间求和:询问区间[a,b]的和
同样把区间[a,b]定在lrt上面,然后直接调用sum[lrt]就行了。那么如何维护sum[x]呢?其实很简单,在向上update的时候就及时更新好了。
inline void update(int x){
sz[x] = sz[lx]+sz[rx]+1;
sum[x] = sum[lx]+sum[rx]+val[x]; // 多了这个
}
inline void Get_sum(){
int a,b;
scanf("%d%d",&a,&b);
int x = get_Kth(root,a);
int y = get_Kth(root,b+2);
splay(x);
splay(y,root);
printf("%d\n",sum[lrt]);
}
7.区间求最值:询问区间[a,b]的最值
那这个是不是跟sum的操作基本一样呢?代码略了。
8.求和的最大子序列:询问区间[a,b]的最大子段和
想法是:对于每个节点,用数组mmax[]来表示以该节点为根的区间的最大子段和。所以我们可以把区间[a,b]定格在lrt,然后直接输出mmax[lrt]。如何维护???
我们可以在update的时候更新!我们维护一个最大前驱和lmax,一个最大后继和rmax,然后再维护一个mmax,然后我们像以下代码这样维护就好了。
inline void update(int x){
sz[x] = sz[lx]+sz[rx]+1;
//sum[x] = sum[lx]+sum[rx]+val[x];
lmax[x] = max( lmax[lx] , sum[lx]+val[x]+max(0,lmax[rx]) );
rmax[x] = max( rmax[rx] , sum[rx]+val[x]+max(0,rmax[lx]) );
mmax[x] = max( mmax[lx] , mmax[rx] );
mmax[x] = max( mmax[x] , max(0,lmax[rx])+val[x]+max(0,rmax[lx]) );
}
inline void Get_max(){
int a,b;
int x = get_Kth(root,a);
int y = get_Kth(root,b+2);
splay(x);
splay(y,root);
printf("%d\n",mmax[lrt]);
}
可能你会想为什么是这么更新的?这样对的吗?想想,我们的splay是中序遍历的,然后画个图出来看看吧。
9.单点修改操作
这个很简单吧,比如我们需要把第k个元素做修改,首先我们找到第k+1,然后旋转至根,直接把根的值修改,update(root)就好了。代码略。。。
至于完整的模板,其实以那题NOI2005维修数列作为模板就好了。
貌似其他的操作暂时我也没做到。就这些吧。。。以下是实战区。。。(点击有链接)
BZOJ 1588: [HNOI2002]营业额统计
这题是我自己的第一道splay题,只是用来测试一下模板的。。。
分析:
这题就是求前驱和后继。旋转跟SBT、AVL、BST、treap一样,并且不需要维护树的平衡,默认为90%的询问发生在10%的数据上面,所以我们在插入的时候就把插入的的数据经过splay操作旋转该节点至根部。这题在询问x的前驱的时候,我们可以先得到x,然后再把x经过splay操作旋转至根部,再找到根的前驱,后继同理。其他的操作以后再总结一下。。
/*
这题是我自己的第一道splay题,只是用来测试一下模板的。。。
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1588
分析:
这题就是求前驱和后继。旋转跟SBT、AVL、BST、treap一样,并且不需要维护树的平衡,
默认为90%的询问发生在10%的数据上面,所以我们在插入的时候就把插入的的数据经过
splay操作旋转该节点至根部。这题在询问x的前驱的时候,我们可以先得到x,然后再把x
经过splay操作旋转至根部,再找到根的前驱,后继同理。其他的操作以后再总结一下。。
*/
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define debug puts("here")
#define rep(i,n) for(int i=0;i<n;i++)
#define foreach(i,vec) for(unsigned i=0;i<vec.size();i++)
#define pb push_back
#define INF 1e9
namespace Splay{
#define X 1111111
#define px pre[x]
#define py pre[y]
#define lx ch[x][0]
#define ly ch[y][0]
#define lz ch[z][0]
#define rx ch[x][1]
#define ry ch[y][1]
int root,tot;
int ch[X][2],pre[X],val[X];
inline void init(){ // 初始化
root = tot = 0;
memset(ch,0,sizeof(ch));
memset(pre,0,sizeof(pre));
memset(val,0,sizeof(val));
}
inline void dfs(int x){ // debug使用
if(x){
dfs(lx);
printf("self = %d , left = %d , right = %d , father = %d\n",x,lx,rx,px);
dfs(rx);
}
}
inline void new_node(int &x,int father,int v){ // 构造新节点
x = ++tot;
pre[x] = father;
val[x] = v;
ch[x][0] = ch[x][1] = 0;
}
inline void setc(int y,int d,int x){ // 旋转过程的子树的链接
ch[y][d] = x;
pre[x] = y;
}
inline int sgn(int x){ // 0表示在左,1表示在右
return ch[px][1]==x;
}
inline void _rot(int x,int d){
int y = px;
int z = py;
setc(y,!d,ch[x][d]);//类似SBT,要把其中一个分支先给父节点
if(z) //如果父节点不是根结点,则要和父节点的父节点连接起来
setc(z,sgn(y),x);
pre[x] = z;
setc(x,d,y);
}
inline void rot(int x){_rot(x,!sgn(x));}
inline void zag(int x){_rot(x,0);} // 左旋
inline void zig(int x){_rot(x,1);} // 右旋
// Splay调整,将根为r的子树调整为goal
inline int splay(int x,int goal=0){
while(px!=goal){
int y = px;
int z = py;
if(z==goal){
rot(x);
break;
}
if(lz==y){
if(ly==x)
zig(y),zig(x);
else
zag(x),zig(x);
}
else{
if(ry==x)
zag(y),zag(x);
else
zig(x),zag(x);
}
}
if(goal==0)
root = x;
return x;
}
inline int insert(int v){ // 插入
int x = root;
while(ch[x][ val[x]<v ]){
if(val[x]==v){
splay(x); // 已存在,这题可以忽略掉,但是需要旋转该节点作为根
return 0;
}
x = ch[x][ val[x]<v ];
}
new_node(ch[x][ val[x]<v ],x,v);
splay(ch[x][ val[x]<v ]); // 新插入节点splay至根部
return 1;
}
inline int get_pre(int x){ // 得到前驱
int tmp = x;
x = lx;
if(x==0)
return INF;
while(rx)
x = rx;
return val[tmp]-val[x];
}
inline int get_next(int x){ // 得到后继
int tmp = x;
x = rx;
if(x==0)
return INF;
while(lx)
x = lx;
return val[x]-val[tmp];
}
#undef X
#undef px
#undef py
#undef lx
#undef ly
#undef lz
#undef rx
#undef ry
}using namespace Splay;
int main(){
#ifndef ONLINE_JUDGE
freopen("sum.in","r",stdin);
#endif
int ans = 0,n,x;
cin >> n;
init();
rep(i,n){
if(scanf("%d",&x)==EOF)
x = 0;
if(i==0){
ans += x;
new_node(root,0,x);
continue;
}
if(insert(x)==0)
continue;
// 前面插入的时候x已经splay至根部,所以可以直接求值
ans += min(get_pre(root),get_next(root));
}
cout<<ans<<endl;
return 0;
}
SGU 187. Twist and whirl - want to cheat
对于数列,求m次区间翻转之后的数列(比较简单。。。)
/*
题目:对于数列,求m次区间翻转之后的数列
分析:splay简单操作
*/
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define debug puts("here")
#define rep(i,n) for(int i=0;i<n;i++)
#define foreach(i,vec) for(unsigned i=0;i<vec.size();i++)
#define pb push_back
#define RD(n) scanf("%d",&n)
namespace Splay{
#define lx ch[x][0]
#define rx ch[x][1]
#define px pre[x]
#define ly ch[y][0]
#define ry ch[y][1]
#define py pre[y]
#define lz ch[z][0]
#define rt ch[root][1]
#define lrt ch[rt][0]
const int MAXN = 130015;
int ch[MAXN][2],pre[MAXN],val[MAXN],sz[MAXN];
bool rev[MAXN];
int root,tot;
int ary[MAXN];
bool ok;
inline void update(int x){ // ok
sz[x] = sz[lx]+sz[rx]+1;
}
inline void new_node(int &x,int y,int v){ // ok
x = ++tot;
px = y;
rev[x] = 0;
lx = rx = 0;
val[x] = v;
}
inline void build(int &x,int y,int l,int r){ // ok
if(l>r) return;
int mid = (l+r)>>1;
new_node(x,y,ary[mid]);
build(lx,x,l,mid-1);
build(rx,x,mid+1,r);
update(x);
}
inline void push_down(int x){ // ok
if(rev[x]==0) return;
swap(lx,rx);
rev[x] = 0;
rev[lx] ^= 1;
rev[rx] ^= 1;
}
inline int sgn(int x){ // ok
return ch[px][1]==x;
}
inline void setc(int y,int d,int x){ // ok
ch[y][d] = x;
px = y;
}
inline void rot(int x,int d){ // ok
int y = px;
int z = py;
push_down(y);
push_down(x);
setc(y,!d,ch[x][d]);
if(z) setc(z,sgn(y),x);
pre[x] = z;
setc(x,d,y);
update(y);
}
inline void splay(int x,int goal=0){ // ok
push_down(x);
while(px!=goal){
int y = px;
int z = py;
if(z==goal){
rot(x,!sgn(x));
break;
}
if(lz==y){
if(ly==x)
rot(y,1),rot(x,1);
else
rot(x,0),rot(x,1);
}
else{
if(ry==x)
rot(y,0),rot(x,0);
else
rot(x,1),rot(x,0);
}
}
update(x);
if(goal==0)
root = x;
}
inline int get_Kth(int x,int k){ // ok
push_down(x);
int tmp = sz[lx]+1;
if(tmp==k) return x;
if(k<tmp) return get_Kth(lx,k);
else return get_Kth(rx,k-tmp);
}
inline void dfs(int x){ // 调试用的,忽略。。
if(x==0) return;
push_down(x);
dfs(lx);
if(val[x]){
ok?printf(" "):ok = 1;
printf("%d",val[x]);
}
dfs(rx);
}
inline void solve(){
ok = 0;
int n,m;
scanf("%d%d",&n,&m);
int x,y;
root = tot = 0;
ch[0][0] = ch[0][1] = pre[0] = rev[0] = 0;
ary[0] = 0;
for(int i=1;i<=n;i++)
ary[i] = i;
new_node(root,0,0);
new_node(rt,root,0);
build(lrt,rt,1,n);
while(m--){
scanf("%d%d",&x,&y);
x = get_Kth(root,x);
splay(x);
y = get_Kth(root,y+2);
splay(y,root);
rev[lrt] ^= 1;
}
dfs(root);
puts("");
}
}using namespace Splay;
int main(){
#ifndef ONLINE_JUDGE
freopen("sum.in","r",stdin);
#endif
solve();
return 0;
}
其实这题都是基本的操作,不过需要注意细节(我自己调了5小时,因为有一处没有及时update...)
/*
分析:
动态计算LCP。
静态的话,可以用后缀数组来计算,但是对于动态的话就无能为力了。。。
如果用询问来计算LCP的话,会TLE的,所以我们可以想到用二分+hash来计
算。但是计算hash的话,由于是动态并且是区间统计问题,所以我们可以用
splay节点表示字符,并且记录以该节点为根的子树所在的区间的字符串的
hash值,判断是否相等就行了。。。
hash函数的选取,用RKhash:
hash[a,b] = ch[a]*1+ch[a+1]*27+...+ch[b]*27^(b-a)
需要注意的是:
1.对于首字符都不同的话,直接输出0
2.短字符串可能是长字符串的前缀
*/
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define debug puts("here")
#define rep(i,n) for(int i=0;i<n;i++)
#define REP(i,a,b) for(int i=a;i<b;i++)
#define foreach(i,vec) for(unsigned i=0;i<vec.size();i++)
#define pb push_back
#define RD(n) scanf("%d",&n)
namespace Splay{
#define lx ch[x][0]
#define rx ch[x][1]
#define px pre[x]
#define ly ch[y][0]
#define ry ch[y][1]
#define py pre[y]
#define lz ch[z][0]
#define rt ch[root][1]
#define lrt ch[rt][0]
const int MAXN = 1e5+10;
char str[MAXN];
const int MOD = 9875321;
int ch[MAXN][2],pre[MAXN],sz[MAXN];
int ha[MAXN],val[MAXN];
int p[MAXN];
int root,tot;
inline void update(int x){ // ok
sz[x] = sz[lx]+sz[rx]+1;
if(sz[0]) puts("error !!! sz[0] != 0");
ha[x] = ll(ha[lx]+(ll)val[x]*p[ sz[lx] ]+(ll)ha[rx]*p[ sz[lx]+1 ])%MOD;
}
inline int sgn(int x){ // ok
return ch[px][1]==x;
}
inline void setc(int y,int d,int x){ // ok
ch[y][d] = x;
px = y;
}
inline void rot(int x,int d){ // ok
int y = px;
int z = py;
setc(y,!d,ch[x][d]);
if(z) setc(z,sgn(y),x);
px = z;
setc(x,d,y);
update(y);
}
inline void splay(int x,int goal=0){ // ok
if(!x)
return;
while(px!=goal){
int y = px;
int z = py;
if(z==goal){
rot(x,!sgn(x));
break;
}
if(lz==y){
ly==x?rot(y,1):rot(x,0);
rot(x,1);
}
else{
ry==x?rot(y,0):rot(x,1);
rot(x,0);
}
}
update(x);
if(goal==0)
root = x;
}
inline int get_Kth(int x,int k){ // ok
int tmp = sz[lx]+1;
if(tmp==k) return x;
return k<tmp?get_Kth(lx,k):get_Kth(rx,k-tmp);
}
inline int get_Min(int x){ // ok
while(lx) x = lx;
return x;
}
inline void new_node(int &x,int y,int c){ // ok
x = ++tot;
ha[x] = val[x] = c;
px = y;
lx = rx = 0;
}
inline void build(int &x,int y,int l,int r){ // ok
if(l>r) return;
int mid = (l+r)>>1;
new_node(x,y,str[mid]-'a'+1);
build(lx,x,l,mid-1);
build(rx,x,mid+1,r);
update(x);
}
void dfs(int x){ // ok
if(x){
cout<<x<<" "<<lx<<" "<<rx<<" "<<sz[x]<<" "<<char(val[x]+'a'-1)<<endl;
dfs(lx);
dfs(rx);
}
}
inline void init(){ // ok
//memset(ch,0,sizeof(ch));
//memset(sz,0,sizeof(sz));
//memset(pre,0,sizeof(pre));
//memset(ha,0,sizeof(ha));
p[0] = 1;
for(int i=1;i<MAXN;i++)
p[i] = (ll)p[i-1]*27%MOD;
root = tot = 0;
new_node(root,0,0);
new_node(rt,root,0);
update(rt);
update(root);
gets(str);
int n = strlen(str);
build(lrt,rt,0,n-1);
update(rt);
update(root);
}
int has(int x,int y){ // ok
splay( get_Kth(root,x) );
splay( get_Kth(root,y+2),root );
return ha[lrt];
}
inline void Q(){
//puts("-------------------------------");
int x,y;
scanf("%d%d",&x,&y);
if(x==y){
printf("%d\n",tot-2-y+1);
return;
}
if(x>y) swap(x,y);
splay( get_Kth(root,x+1) );
int tmp = val[root];
splay( get_Kth(root,y+1) );
//dfs(root);
//cout<<"k = "<<get_Kth(root,y+1)<<endl;
//cout<<"tot = "<<tot<<endl;
//cout<<dx<<" "<<dy<<endl;
if(val[root] != tmp ){
puts("0");
return;
}
//cout<<"------- "<<tot-2-y+1<<endl;
if(tot-2-y+1==1){
puts("1");
return;
}
//debug;
int len = tot-2-y;
if( has(x,x+len)==has(y,y+len) ){
printf("%d\n",tot-2-y+1);
return;
}
//debug;
int l = 1,r = tot-2-y;
while(l<=r){
int mid = (l+r)>>1;
if(has(x,x+mid)==has(y,y+mid))
l = mid+1;
else
r = mid-1;
}
printf("%d\n",l);
}
inline void R(){ // ok
char s[2];
int x;
scanf("%d%s",&x,s);
splay( get_Kth(root,x+1) );
val[root] = s[0]-'a'+1;
update(root);
}
inline void I(){ // ok
int x;
char s[2];
scanf("%d%s",&x,s);
x = get_Kth(root,x+1);
//cout<<"dsadsa "<<x<<endl;
splay(x);
int y = get_Min(rt);
splay(y,root);
//cout<<y<<endl;
new_node(lrt,rt,s[0]-'a'+1);
update(lrt);
update(rt);
update(root);
//dfs(root);
}
inline void solve(){ // ok
init();
//dfs(root);
char op[2];
int m;
cin>>m;
while(m--){
scanf("%s",op);
if(op[0]=='Q') Q();
else if(op[0]=='R') R();
else I();
}
}
};
int main(){
#ifndef ONLINE_JUDGE
freopen("sum.in","r",stdin);
//freopen("sum.out","w",stdout);
#endif
Splay::solve();
return 0;
}
BZOJ 1269 [AHOI2006]文本编辑器editor
/*
题目:支持 插入,翻转,删除的功能。
分析:splay的基本操作。
1.插入,我们需要把插入的位置伸展到根,然后再把伸展之后的后一个位置伸展到根的
右儿子,插入就直接把所有的数插入到根的右儿子的左子树中。
2.删除,我们把需要删除的整个区间[a,b]直接伸展到根的右儿子的左子树,即把a-1
伸展到根,把b+1伸展到根的右儿子,然后把根的右儿子的左子树整个删除掉即可
3.旋转,lazy标记,每当我们往下遍历的时候,需要把lazy标记下沉。而旋转过程中,比如
我们需要对区间[a,b]旋转,我们可以把a-1旋转到根,把b+1旋转到根的右儿子,然后把根的
右儿子的左子树的标记翻转一下就行了。
4.输出,我们需要输出光标后的第一个字符,我们可以直接把光标的位置伸展到根,然后再
求根的右儿子的最小值输出
5.移动到第k个字符后面,我们直接把光标改变,无需把该位置伸展到根,只需要用的时候
再进行伸展操作
6.前移(后移),直接把光标减一(加一),无需进行伸展操作
*/
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define debug puts("here")
#define rep(i,n) for(int i=0;i<n;i++)
#define foreach(i,vec) for(unsigned i=0;i<vec.size();i++)
#define pb push_back
int tot,root,pos;
namespace Splay{
#define lx ch[x][0]
#define rx ch[x][1]
#define px pre[x]
#define ly ch[y][0]
#define ry ch[y][1]
#define py pre[y]
#define lz ch[z][0]
#define rz ch[z][1]
#define pz pre[z]
#define rt ch[root][1]
#define lrt ch[rt][0]
const int X = (1<<21)+5;
int ch[X][2],pre[X],sz[X],rev[X];
char str[X];
char qq[X];
inline void dfs(int x){
if(x){
dfs(lx);
cout<<lx<<" "<<rx<<" "<<x<<endl;
dfs(rx);
}
}
inline void update(int x){
//cout<<lx<<" "<<rx<<" "<<sz[lx]<<" "<<sz[rx]<<" "<<sz[lx]+sz[rx]+1<<endl;
sz[x] = sz[lx]+sz[rx]+1;
}
inline void push_down(int x){
if(x&&rev[x]){
swap(lx,rx);
rev[lx] ^= 1;
rev[rx] ^= 1;
rev[x] = 0;
}
}
inline void new_node(int &x,int y,char c){
x = ++tot;
pre[x] = y;
rev[x] = ch[x][0] = ch[x][1] = 0;
str[x] = c;
}
inline void build(int &x,int l,int r,int y,char *s){
if(l>r) return;
int mid = (l+r)>>1;
new_node(x,y,s[mid]);
build(lx,l,mid-1,x,s);
build(rx,mid+1,r,x,s);
update(x);
}
inline void setc(int y,int d,int x){
ch[y][d] = x;
pre[x] = y;
}
inline int sgn(int x){
return ch[px][1]==x;
}
inline void _rot(int x,int d){
int y = px;
int z = py;
push_down(y);
push_down(x);
setc(y,!d,ch[x][d]);
if(z) setc(z,sgn(y),x);
pre[x] = z;
setc(x,d,y);
update(y);
}
inline void rot(int x){_rot(x,!sgn(x));}
inline void zag(int x){_rot(x,0);}
inline void zig(int x){_rot(x,1);}
inline int splay(int x,int goal=0){
push_down(x);
while(px!=goal){
int y = px;
int z = py;
if(z==goal){
rot(x);
break;
}
if(lz==y){
if(ly==x)
zig(y),zig(x);
else
zag(x),zig(x);
}
else{
if(ry==x)
zag(y),zag(x);
else
zig(x),zag(x);
}
}
update(x);
if(goal==0)
root = x;
return x;
}
inline int get_Kth(int x,int k){
push_down(x);
int tmp = sz[lx]+1;
if(tmp==k)
return x;
if(k<tmp)
return get_Kth(lx,k);
else
return get_Kth(rx,k-tmp);
}
inline int get_min(int x){
push_down(x);
while(lx){
x = lx;
push_down(x);
}
return x;
}
inline void Move(){
int k;
scanf("%d",&k);
pos = k+1;
}
inline void Insert(){
int k;
scanf("%d",&k);
getchar();
gets(qq);
int x = get_Kth(root,pos);
splay(x);
x = get_min(rt);
splay(x,root);
build(lrt,0,k-1,rt,qq);
}
inline void Delete(){
int k;
scanf("%d",&k);
int x = get_Kth(root,pos);
splay(x);
int y = get_Kth(root,pos+k+1);
splay(y,root);
pre[lrt] = 0;
lrt = 0;
update(rt);
update(root);
}
inline void Rotate(){
int k;
scanf("%d",&k);
int x = get_Kth(root,pos);
splay(x);
int y = get_Kth(root,pos+k+1);
splay(y,root);
rev[lrt] ^= 1;
}
inline void Get(){
int x = get_Kth(root,pos);
splay(x);
x = get_min(rt);
printf("%c\n",str[x]);
}
inline void Prev(){
pos --;
}
inline void Next(){
pos ++;
}
inline void init(){
memset(pre,0,sizeof(pre));
memset(ch,0,sizeof(ch));
memset(sz,0,sizeof(sz));
memset(rev,0,sizeof(rev));
root = tot = 0;
char s[] = "@@@@@@";
build(root,0,5,0,s);
pos = 1;
}
}using namespace Splay;
int main(){
#ifndef ONLINE_JUDGE
freopen("sum.in","r",stdin);
//freopen("sum.out","w",stdout);
#endif
int ncase;
char op[20];
cin>>ncase;
init();
while(ncase--){
scanf("%s",op);
switch(op[0]){
case 'M':Move();break;
case 'I':Insert();break;
case 'D':Delete();break;
case 'R':Rotate();break;
case 'G':Get();break;
case 'P':Prev();break;
default:Next();
}
}
return 0;
}
/*
题目:
插入、删除、区间赋值、翻转、求总和、求最大子序列
分析:
前面的都是比较简单的splay操作,而在求最大子序列的时候,我们需要维护
lmax,rmax,mmax三个值,每次下沉的时候都需要更新
*/
#include <set>
#include <map>
#include <cmath>
#include <queue>
#include <stack>
#include <string>
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
#define debug puts("here")
#define rep(i,n) for(int i=0;i<n;i++)
#define foreach(i,vec) for(unsigned i=0;i<vec.size();i++)
#define pb push_back
#define RD(n) scanf("%d",&n)
namespace Splay{
#define lx ch[x][0]
#define rx ch[x][1]
#define px pre[x]
#define ly ch[y][0]
#define ry ch[y][1]
#define py pre[y]
#define lz ch[z][0]
#define rt ch[root][1]
#define lrt ch[rt][0]
const int MAXN = 500005;
const int INF = 1e9;
int n,m;
int root,tot;
int sta[MAXN],top;
int pre[MAXN],ch[MAXN][2],sz[MAXN],val[MAXN];
int sum[MAXN];
int lmax[MAXN],rmax[MAXN],mmax[MAXN];
bool rev[MAXN],same[MAXN];
int aa[MAXN];
inline void update_same(int x,int v){
if(x==0) return;
val[x] = v;
same[x] = true;
sum[x] = sz[x]*v;
mmax[x] = lmax[x] = rmax[x] = max(sum[x],v);
}
inline void update_rev(int x){
if(x==0) return;
rev[x] ^= 1;
swap(lx,rx);
swap(lmax[x],rmax[x]);
}
inline void update(int x){
sz[x] = sz[lx]+sz[rx]+1;
sum[x] = sum[lx]+sum[rx]+val[x];
lmax[x] = max( lmax[lx] , sum[lx]+val[x]+max(0,lmax[rx]) );
rmax[x] = max( rmax[rx] , sum[rx]+val[x]+max(0,rmax[lx]) );
mmax[x] = max( mmax[lx] , mmax[rx] );
mmax[x] = max( mmax[x] , max(0,lmax[rx])+val[x]+max(0,rmax[lx]) );
}
inline void push_down(int x){
if(rev[x]){
update_rev(lx);
update_rev(rx);
rev[x] = 0;
}
if(same[x]){
update_same(lx,val[x]);
update_same(rx,val[x]);
same[x] = 0;
}
}
inline int sgn(int x){
return ch[px][1]==x;
}
inline void setc(int y,int d,int x){
ch[y][d] = x;
px = y;
}
inline void rot(int x,int d){
int y = px;
int z = py;
push_down(y);
push_down(x);
setc(y,!d,ch[x][d]);
if(z) setc(z,sgn(y),x);
pre[x] = z;
setc(x,d,y);
update(y);
}
inline void splay(int x,int goal=0){
push_down(x);
while(px!=goal){
int y = px;
int z = py;
if(z==goal){
rot(x,!sgn(x));
break;
}
if(lz==y){
if(ly==x)
rot(y,1),rot(x,1);
else
rot(x,0),rot(x,1);
}
else{
if(ry==x)
rot(y,0),rot(x,0);
else
rot(x,1),rot(x,0);
}
}
update(x);
if(goal==0)
root = x;
}
inline int get_Kth(int x,int k){
push_down(x);
int tmp = sz[lx]+1;
if(tmp==k)
return x;
if(k<tmp)
return get_Kth(lx,k);
return get_Kth(rx,k-tmp);
}
inline int get_min(int x){
push_down(x);
while(lx){
x = lx;
push_down(x);
}
return x;
}
inline void del(int x){
if(x==0) return;
sta[top++] = x;
del(lx);
del(rx);
}
inline void Delete(){
int pos,k;
scanf("%d%d",&pos,&k);
int x = get_Kth(root,pos);
int y = get_Kth(root,pos+k+1);
splay(x);
splay(y,root);
del(lrt);
pre[lrt] = 0;
lrt = 0;
update(rt);
update(root);
}
inline void Make_same(){
int pos,k,v;
scanf("%d%d%d",&pos,&k,&v);
int x = get_Kth(root,pos);
int y = get_Kth(root,pos+k+1);
splay(x);
splay(y,root);
update_same(lrt,v);
update(rt);
update(root);
}
inline void Make_rev(){
int pos,k;
scanf("%d%d",&pos,&k);
int x = get_Kth(root,pos);
int y = get_Kth(root,pos+k+1);
splay(x);
splay(y,root);
update_rev(lrt);
}
inline void new_node(int &x,int y,int v){
if(top) x = sta[--top]; // 从内存池中取出编号
else x = ++tot; //内存池为空
ch[x][0] = ch[x][1] = 0;
val[x] = sum[x] = lmax[x] = rmax[x] = mmax[x] = v;
rev[x] = same[x] = 0;
pre[x] = y;
}
inline void build(int &x,int y,int l,int r){
if(l>r) return;
int mid = (l+r)>>1;
new_node(x,y,aa[mid]);
build(lx,x,l,mid-1);
build(rx,x,mid+1,r);
update(x);
}
inline void Insert(){
int pos,k;
scanf("%d%d",&pos,&k);
rep(i,k)
RD(aa[i]);
pos ++;
int x = get_Kth(root,pos);
splay(x);
int y = get_min(rt);
splay(y,root);
build(lrt,rt,0,k-1);
update(rt);
update(root);
}
inline void Get_sum(){
int pos,k;
scanf("%d%d",&pos,&k);
int x = get_Kth(root,pos);
int y = get_Kth(root,pos+k+1);
splay(x);
splay(y,root);
printf("%d\n",sum[lrt]);
}
inline void Get_max(){
int pos = 1,k = sz[root]-2;
int x = get_Kth(root,pos);
int y = get_Kth(root,pos+k+1);
splay(x);
splay(y,root);
printf("%d\n",mmax[lrt]);
}
inline void init(){
root = tot = top = 0;
ch[0][0] = ch[0][1] = 0;
pre[0] = sz[0] = 0;
sum[0] = same[0] = rev[0] = 0;
lmax[0] = rmax[0] = mmax[0] = -INF;
new_node(root,0,-1);
new_node(rt,root,-1);
update(rt);
update(root);
RD(n);
RD(m);
rep(i,n)
RD(aa[i]);
build(lrt,rt,0,n-1);
update(rt);
update(root);
}
}using namespace Splay;
int main(){
#ifndef ONLINE_JUDGE
freopen("sum.in","r",stdin);
#endif
init();
char op[20];
while(m--){
scanf("%s",op);
//puts(op);
if(op[0]=='I')
Insert();
else if(op[0]=='D')
Delete();
else if(op[0]=='R')
Make_rev();
else if(op[0]=='G')
Get_sum();
else if(op[2]=='X')
Get_max();
else
Make_same();
}
return 0;
}