说在前面
本来昨天想学LCT,其中有一部分要用到Splay
然后me发现自己好像还没有写过Splay,虽然操作都知道,不过感觉不太稳。于是去找了一道题练练手,然而写了很久也过不了。
想到之前写过一道Treap,想借助那个Treap代码来调试WA掉的Splay
结果不仅Splay没调出来,顺便还发现了那份treap代码的bug,黑人问号脸.jpg = =?
真的当时内心绝望…
不过经过长达一天的努力,终于搞定了!while(1) 开心++,成就感++;
花的时间感觉挺多的。不过呢,把之前初学平衡树时候的很多疑问搞清楚了,还成功地写出了自己的板子,也是值得的
BZOJ3223-文艺平衡树
题面
您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作:翻转一个区间,例如原有序序列是5 4 3 2 1,翻转区间是[2,4]的话,结果是5 2 3 4 1
输入输出格式
输入格式:
第一行为n,m n表示初始序列有n个数,这个序列依次是(1,2……n-1,n) m表示翻转操作次数
接下来m行每行两个数[l,r] 数据保证 1<=l<=r<=n
输出格式:
输出一行n个数字,表示原始序列经过m次变换后的结果
解法
一道比较基础的Splay题,核心是reverse标记
先由下标建好一个Splay
对于每个操作[L,R],把第L-1个节点Splay到根,这时候第R+1个节点一定在当前根的右子树内
然后再把第R+1个节点Splay到L的下方,那么区间[L,R]就是第R+1个节点的左儿子(极为x)以及它的子树。正确性由平衡树定义得到。
在x节点处打一个reverse标记,然后将左右子树互换,像线段树一样用标记推下去。因为这棵Splay是由下标(位置)建的树,所以交换左右儿子相当于是交换了位置。
注意Splay操作的时候,如果是Splay到根,需要同时更新root
自带大常数的代码
/**************************************************************
Problem: 3223
User: Izumihanako
Language: C++
Result: Accepted
Time:2224 ms
Memory:3856 kb
****************************************************************/
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#define loop(i,j,k) for(int i=j;i<=k;i++)
using namespace std;
struct treap{
int siz,val;
bool reser;
treap* ch[2];
treap *fa;
void updata(){
this->siz=1;
if(this->ch[0]!=NULL) this->siz+=this->ch[0]->siz;
if(this->ch[1]!=NULL) this->siz+=this->ch[1]->siz;
}
void pushdown(){
if(this->reser==true){
swap(this->ch[0],this->ch[1]);
this->reser=false;
if(this->ch[0]!=NULL) this->ch[0]->reser^=1;
if(this->ch[1]!=NULL) this->ch[1]->reser^=1;
}
}
treap(treap* k,int x){
this->val=x;
this->fa=k;
this->reser=false;
this->siz=1;
}
};
int n,m;
treap *root=NULL;
void dfs( treap *nd ){
printf( "%d -> %d(%d %d)\n" , nd->fa?nd->fa->val:-1 , nd->val , nd->ch[0]?nd->ch[0]->val:-1 , nd->ch[1]?nd->ch[1]->val:-1 ); ;
if( nd->ch[0] ) dfs( nd->ch[0] ) ;
if( nd->ch[1] ) dfs( nd->ch[1] ) ;
}
treap *build(int L,int R,treap *fa){
if(L>R) return NULL;
int mid=(L+R)/2;
treap *t=new treap(fa,mid);
t->ch[0]=build(L,mid-1,t);
t->ch[1]=build(mid+1,R,t);
t->updata();
return t;
}
void Rotate( treap *&nd , int aim ){
treap *x = nd -> ch[aim] ;
nd -> ch[aim] = x -> ch[aim^1] ;
if( x -> ch[aim^1] ) x -> ch[aim^1] -> fa = nd ;
x -> fa = nd -> fa ;
if( nd->fa ){
if( nd->fa->ch[0] == nd ) nd->fa->ch[0] = x ;
else nd->fa->ch[1] = x ;
}
x -> ch[aim^1] = nd ;
nd -> fa = x ;
nd -> updata() ;
x -> updata() ;
}
inline void Splay( treap *&nd , treap* aim ){
while( nd -> fa != aim ){
treap *fa = nd -> fa , *gdfa = nd -> fa -> fa ;
int pn = ( (nd == fa -> ch[1]) ? 1 : 0 ) , pf ;
if( gdfa && gdfa != aim ){
pf = ( fa == gdfa -> ch[1] ) ;
if( pn == pf ){
Rotate( gdfa , pn ) ;
Rotate( fa , pf ) ;
} else {
Rotate( fa , pn ) ;
Rotate( gdfa , pf ) ;
}
} else
Rotate( fa , pn ) ;
if( nd->fa == aim && aim == NULL ) root = nd ;
}
}
inline treap* Kth( treap *nd , int K ){
nd->pushdown() ;
int Lsiz = ( nd -> ch[0] ? nd -> ch[0] -> siz : 0 ) ;
if( K > Lsiz + 1 ) return Kth( nd -> ch[1] , K - Lsiz - 1 ) ;
if( K > Lsiz && K <= Lsiz + 1 ) return nd ;
return Kth( nd -> ch[0] , K ) ;
}
void reverse(int L,int R){
treap *Lt=Kth(root,L),*Rt=Kth(root,R+2);
Splay(Lt,NULL);
root=Lt;
Splay(Rt,Lt);
if( Rt->ch[0] ) Rt->ch[0]->reser^=1;
}
void print(treap *t){
t->pushdown();
if(t->ch[0]!=NULL) print(t->ch[0]);
if(t->val!=0&&t->val!=n+1) printf("%d ",t->val);
if(t->ch[1]!=NULL) print(t->ch[1]);
}
int main(){
int L,R;
scanf("%d%d",&n,&m);
root=build(0,n+1,NULL);
loop(i,1,m){
scanf("%d%d",&L,&R);
reverse(L,R);
}
print(root);
return 0;
}
BZOJ3224普通平衡树
题面
您需要写一种数据结构(可参考题目标题),来维护一些数,其中需要提供以下操作:
1. 插入x数
2. 删除x数(若有多个相同的数,因只删除一个)
3. 查询x数的排名(若有多个相同的数,因输出最小的排名)
4. 查询排名为x的数
5. 求x的前驱(前驱定义为小于x,且最大的数)
6. 求x的后继(后继定义为大于x,且最小的数)
输入输出格式
输入格式:
第一行为n,表示操作的个数,下面n行每行有两个数opt和x,opt表示操作的序号(1<=opt<=6)
输出格式:
对于操作3,4,5,6每行输出一个数,表示对应答案
解法
这道题就是一个比较全的板子题了,有很多平衡树的基本操作。
注意这个删除操作的处理,当一个点的出现次数多于一次时,直接cnt–即可
而只有一次时,则需要将该点从树中移除掉。me的办法是先把要删除的点(记为x)Splay到根,然后在左子树里找到当前根的前驱(记为y),记录下来之后把x删掉。然后把y点Splay到根,把原来的右子树接到y的右子树上去。(因为y是x的前驱,也就是原左子树的最大值,Splay之后y没有右子树,所以可以直接接过去 )
自带大常数的代码
/**************************************************************
Problem: 3224
User: Izumihanako
Language: C++
Result: Accepted
Time:516 ms
Memory:2284 kb
****************************************************************/
#include "cstdio"
#include "cstring"
#include "algorithm"
#include "cstdlib"
struct Node{
Node *ch[2],*fa;
int cnt,val,fix,siz;
Node(int x){
this->val=x;
fix=rand();
siz=cnt=0;
fa=ch[0]=ch[1]=NULL;
}
int dcmp(int x){
if(x==val) return -1;
return x > val ? 1 : 0;
}
void update(){
siz=this->cnt;
if(ch[0]) siz+=ch[0]->siz;
if(ch[1]) siz+=ch[1]->siz;
}
} *root , *nnode ;
int n,d,x,beh,fro,tmp,ok;
inline void Insert( Node *&nd , int num ) ;
inline void Rotate( Node *&nd , int aim ) ;
inline void Splay( Node *&nd ) ;
inline void Delete( Node* nd , int num ) ;
void dfs( Node *nd ){
printf( "%d -> %d(%d %d)\n" , nd->fa?nd->fa->val:-1 , nd->val , nd->ch[0]?nd->ch[0]->val:-1 , nd->ch[1]?nd->ch[1]->val:-1 ); ;
if( nd->ch[0] ) dfs( nd->ch[0] ) ;
//printf( "%d " , nd->val ) ;
if( nd->ch[1] ) dfs( nd->ch[1] ) ;
}
void Rotate( Node *&nd , int aim ){
Node *x = nd -> ch[aim] ;
// printf( "Rotate : fa %d aim %d\n" , nd->val , x->val ) ;
nd -> ch[aim] = x -> ch[aim^1] ;
if( x -> ch[aim^1] ) x -> ch[aim^1] -> fa = nd ;
x -> fa = nd -> fa ;
if( nd->fa ){
//printf( "Rotate : now %d arc %d x %d\n" , nd , nd->fa->ch[0] , x ) ;
if( nd == nd->fa->ch[0] ) nd->fa->ch[0] = x ;
else nd->fa->ch[1] = x ;
//printf( "Rotate : now %d arc %d x %d\n" , nd , nd->fa->ch[0] , x ) ;
}
x -> ch[aim^1] = nd ;
nd -> fa = x ;
nd -> update() ;
x -> update() ;
}
inline void Insert( Node *&nd , int num ){
if( !nd )
nd = new Node( num ) ;
int aim = nd -> dcmp( num ) ;
if( aim == -1 ){
nd -> cnt ++ , nd -> siz ++ ;
nnode = nd ;
return ;
} else {
Insert( nd -> ch[aim] , num ) ;
nd -> ch[aim] -> fa = nd ;
nd -> update() ;
}
}
inline void Splay( Node *&nd ){
while( nd -> fa != NULL ){
Node *fa = nd -> fa , *gdfa = nd -> fa -> fa ;
int pn = ( (nd == fa -> ch[1]) ? 1 : 0 ) , pf ;
if( gdfa ){
pf = ( fa == gdfa -> ch[1] ) ;
if( pn == pf ){
Rotate( gdfa , pn ) ;
Rotate( fa , pf ) ;
} else {
Rotate( fa , pn ) ;
Rotate( gdfa , pf ) ;
}
} else
Rotate( fa , pn ) ;
}
if( nd->fa == NULL ) root = nd ;
}
inline void Delete( Node* nd , int num ){
if( !nd ) return ;
int aim = nd -> dcmp( num ) ;
if( aim == -1 ){
if( nd -> cnt != 1 ){
nd -> cnt -- , nd -> siz -- ;
return ;
} else {
Splay( nd ) ;
Node *tmp = nd -> ch[0] ;
if( !tmp ) root = nd -> ch[1] , nd -> ch[1] -> fa = NULL ;
else{
while( tmp -> ch[1] ) tmp = tmp -> ch[1] ;
nd -> ch[0] -> fa = NULL ; Splay( tmp ) ;
if( nd -> ch[1] ){
tmp -> ch[1] = nd -> ch[1] ;
nd -> ch[1] -> fa = tmp ;
}
tmp -> update() ;
}
}
} else{
nd -> siz -- ;
Delete( nd -> ch[aim] , num ) ;
}
if( nd ) nd->update() ;
}
int Rank(Node* t,int x){
if( !t ) return 0x3f3f3f3f ;
int d = t->dcmp(x),delta;
if(t->ch[0]==NULL) delta=0;
else delta=t->ch[0]->siz;
if(d==-1){
Splay( t ) ;
return delta+1 ;
}
else if(d==0) return Rank(t->ch[d],x);
else return Rank(t->ch[1],x)+delta+t->cnt;
}
inline int Kth( Node *nd , int K ){
int Lsiz = ( nd -> ch[0] ? nd -> ch[0] -> siz : 0 ) ;
// printf( "Kth :nd(%d %d) K(%d) Lsiz(%d)\n" , nd , nd -> num , K , Lsiz ) ;
if( K > Lsiz + nd -> cnt ) return Kth( nd -> ch[1] , K - Lsiz - nd -> cnt ) ;
else if( K > Lsiz && K <= Lsiz + nd -> cnt ){
Splay( nd ) ;
return nd -> val ;
}
else return Kth( nd -> ch[0] , K ) ;
}
void be(Node *t,int x){
if(t==NULL) return;
int d=t->dcmp(x);
if(d==1||d==-1) be(t->ch[1],x);
else if(d==0) {beh=t->val;be(t->ch[0],x);}
}
void fr(Node *t,int x){
if(t==NULL) return;
int d=t->dcmp(x);
if(d==1) {fro=t->val;fr(t->ch[1],x);}
else if(d==0||d==-1) {fr(t->ch[0],x);}
}
inline int read()
{
int data=0,w=1; char ch=0;
while(ch!='-' && (ch<'0' || ch>'9')) ch=getchar();
if(ch=='-') w=-1,ch=getchar();
while(ch>='0' && ch<='9') data=data*10+ch-'0',ch=getchar();
return data*w;
}
int main(){
n=read();
for(int i=1;i<=n;i++){
d=read();x=read();beh=0;fro=0;
switch(d){
case 1:Insert(root,x); Splay( nnode ) ; break;
case 2:Delete(root,x);break;
case 3:printf("%d\n",Rank(root,x));break;
case 4:printf("%d\n",Kth(root,x));break;
case 5:fr(root,x);printf("%d\n",fro);break;
case 6:be(root,x);printf("%d\n",beh);break;
}
}
return 0;
}
POJ2761
题目大意
给定一个序列和一些询问。每次讯问[L,R]中第k大的数字。输入保证[L,R]不包含。
输入输出格式
输入格式:
第一行包含两个整数N,M,表示序列长度和询问数
接下来M行每行三个数L,R,K,含义如题所述
输出格式:
对于每个询问,输出一个数字表示答案
解法
这道题可以直接上主席树在线搞
不过呢,注意到输入保证讯问的区间不会包含,因此可以离线用Splay等平衡树处理
把所有的询问按照L排序,那么R也一定是单增的,由于题目的保证,这是显然的
现在假设我们已经处理了上一个询问,现在需要处理[L,R]的询问,我们把当前询问没有包含的数字从平衡树里delete掉,然后insert平衡树里还没有的数字,剩下的就是常规查询第K大了
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;
int N , M , ori[100005] , ans[50005] ;
struct Queries{
int L , R , K , id ;
bool operator < ( const Queries &A ) const {
return L < A.L ||
(L == A.L && R <= A.R ) ;
}
}Q[50005] ;
struct Node{
int num , cnt , tot ;
Node *ch[2] , *fa ;
Node( int num_ ){
num = num_ ;
cnt = tot = 0 ;
ch[0] = ch[1] = fa = 0 ;
}
int dcmp( const int &tmp ){
if( tmp == num ) return -1 ;
return tmp > num ;
}
void update(){
tot = cnt ;
if( ch[0] ) tot += ch[0] -> tot ;
if( ch[1] ) tot += ch[1] -> tot ;
}
}*root , *nnode ;
inline void Insert( Node *&nd , int num ) ;
inline void Rotate( Node *&nd , int aim ) ;
inline void Splay( Node *&nd ) ;
inline void Delete( Node* nd , int num ) ;
inline int Kth( Node *nd , int K ) ;
inline void Insert( Node *&nd , int num ){
if( !nd )
nd = new Node( num ) ;
int aim = nd -> dcmp( num ) ;
if( aim == -1 ){
nd -> cnt ++ , nd -> tot ++ ;
nnode = nd ;
return ;
} else {
Insert( nd -> ch[aim] , num ) ;
nd -> ch[aim] -> fa = nd ;
nd -> update() ;
}
}
inline void Rotate( Node *&nd , int aim ){
Node *x = nd -> ch[aim] ;
nd -> ch[aim] = x -> ch[aim^1] ;
if( x -> ch[aim^1] ) x -> ch[aim^1] -> fa = nd ;
x -> fa = nd -> fa ;
if( nd->fa ){
if( nd == nd->fa->ch[0] ) nd->fa->ch[0] = x ;
else nd->fa->ch[1] = x ;
}
x -> ch[aim^1] = nd ;
nd -> fa = x ;
nd -> update() ;
x -> update() ;
}
inline void Splay( Node *&nd ){
while( nd -> fa != NULL){
Node *fa = nd -> fa , *gdfa = nd -> fa -> fa ;
int pn = ( (nd == fa -> ch[1]) ? 1 : 0 ) , pf ;
if( gdfa ){
pf = ( fa == gdfa -> ch[1] ) ;
if( pn == pf ){
Rotate( gdfa , pn ) ;
Rotate( fa , pf ) ;
} else {
Rotate( fa , pn ) ;
Rotate( gdfa , pf ) ;
}
} else
Rotate( fa , pn ) ;
}
if( nd->fa == NULL ) root = nd ;
}
inline void Delete( Node* nd , int num ){
if( !nd ) return ;
int aim = nd -> dcmp( num ) ;
if( aim == -1 ){
if( nd -> cnt != 1 ){
nd -> cnt -- , nd -> tot -- ;
return ;
} else {
Splay( nd ) ;
Node *tmp = nd -> ch[0] ;
if( !tmp ) root = nd -> ch[1] , nd -> ch[1] -> fa = NULL ;
else{
while( tmp -> ch[1] ) tmp = tmp -> ch[1] ;
nd -> ch[0] -> fa = NULL ; Splay( tmp ) ;
if( nd -> ch[1] ){
tmp -> ch[1] = nd -> ch[1] ;
nd -> ch[1] -> fa = tmp ;
}
tmp -> update() ;
}
}
} else{
nd -> tot -- ;
Delete( nd -> ch[aim] , num ) ;
}
if( nd ) nd->update() ;
}
inline int Kth( Node *nd , int K ){
int Lsiz = ( nd -> ch[0] ? nd -> ch[0] -> tot : 0 ) ;
if( K > Lsiz + nd -> cnt ) return Kth( nd -> ch[1] , K - Lsiz - nd -> cnt ) ;
else if( K > Lsiz && K <= Lsiz + nd -> cnt ){
Splay( nd ) ;
return nd -> num ;
}
else return Kth( nd -> ch[0] , K ) ;
}
void solve(){
sort( Q + 1 , Q + M + 1 ) ;
int lf = 1 , rg = 0 ;
for( register int i = 1 ; i <= M ; i ++ ){
int L = Q[i].L , R = Q[i].R ;
while( rg < R ){
rg ++ ;
Insert( root , ori[rg] ) ; Splay( nnode ) ;
}
while( lf < L ){
Delete( root , ori[lf] ) ;
lf ++ ;
}
ans[Q[i].id] = Kth( root , Q[i].K ) ;
}
for( register int i = 1 ; i <= M ; i ++ )
printf( "%d\n" , ans[i] ) ;
}
inline int read_(){
int rt = 0;
char ch = getchar() ;
while( ch < '0' || ch > '9' ) ch = getchar() ;
while( ch >='0' && ch <='9' ) rt = rt * 10 + ch - '0' , ch = getchar() ;
return rt;
}
int main(){
scanf( "%d%d" , &N , &M ) ;
for( int i = 1 ; i <= N ; i ++ )
ori[i] = read_() ;
for( int i = 1 ; i <= M ; i ++ ){
Q[i].L = read_() , Q[i].R = read_() , Q[i].K = read_() ;
Q[i].id = i ;
}
solve() ;
}