splay总结
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; }