codevs 4655序列終結者
Splay樹是一個優化過的二叉搜索樹,所以它滿足二叉搜索樹的性質,二叉搜索樹有可能退化爲一條鏈,n^2的時間複雜度,而Splay經過一系列的旋轉操作使其平均複雜度控制在log(2)n但是常數比較大,因爲要進行一系列的旋轉操作。
/*
因爲Splay_tree滿足排序二叉樹的性質,所以當查詢或者修改某一個[L,R]區間的時候,
就可以將L旋轉到根的位置,將R旋轉到根的右節點位置這樣R的左節點電錶的區間就是[L,R]這個區間。
建樹的時候從1到n+2每個節點代表一個節點滿足左節點小於根,右節點大於根所以纔會出現這種建樹方式,
每一棵子樹代表一個區間。
splay操作即伸展操作,將某個節點通過一系列的旋轉到達固定節點的子節點位置。
rotate操作即是每一步具體的旋轉操作
*/
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;
const int maxn=1e6+10;
const int inf=0x7fffffff;
struct Splay_tree
{
int root;
struct zp
{
int son[2],Size,fa,lazy,rev,Max,val;//左右兒子 ,子樹大小,父節點位置,延遲更新標記,反轉標記,子樹最大值,節點值
void init(int _val)
{
Size=1,Max=val=_val;
rev=lazy=son[0]=son[1]=0;
}
} T[maxn];
void push_up(int x)
{
T[x].Size=1,T[x].Max=T[x].val;
if(T[x].son[0])
{
T[x].Max=max(T[x].Max,T[T[x].son[0]].Max);
T[x].Size+=T[T[x].son[0]].Size;
}
if(T[x].son[1])
{
T[x].Max=max(T[x].Max,T[T[x].son[1]].Max);
T[x].Size+=T[T[x].son[1]].Size;
}
}
void push_down(int x)
{
if(x==0) return ;
if(T[x].lazy)
{
if(T[x].son[0])
{
T[T[x].son[0]].lazy+=T[x].lazy;
T[T[x].son[0]].Max+=T[x].lazy;
T[T[x].son[0]].val+=T[x].lazy;
}
if(T[x].son[1])
{
T[T[x].son[1]].lazy+=T[x].lazy;
T[T[x].son[1]].Max+=T[x].lazy;
T[T[x].son[1]].val+=T[x].lazy;
}
T[x].lazy=0;
}
if(T[x].rev)
{
if(T[x].son[0])
T[T[x].son[0]].rev^=1;
if(T[x].son[1])
T[T[x].son[1]].rev^=1;
swap(T[x].son[0],T[x].son[1]);
T[x].rev=0;
}
}
void rotate(int x,int d)//0 左旋 1右旋
{
int y=T[x].fa,z=T[y].fa;
T[y].son[!d]=T[x].son[d];
T[T[x].son[d]].fa=y;
T[x].son[d]=y;
T[y].fa=x;
T[z].son[T[z].son[1]==y]=x;
T[x].fa=z;
push_up(y);
}
void splay(int x,int goal)//將x旋轉到goal的子節點的位置 伸展操作
{
if(x==goal) return ;
while(T[x].fa!=goal)
{
int y=T[x].fa,z=T[y].fa;
push_down(z),push_down(y),push_down(x);
int rx=T[y].son[0]==x,ry=T[z].son[0]==y;//rx是x節點的旋轉方向,ry是y節點的旋轉方向
if(z==goal) rotate(x,rx);
else
{
if(rx==ry) rotate(y,rx);
else rotate(x,rx);
rotate(x,ry);
}
}
push_up(x);
if(goal==0) root=x;
}
int Select(int pos)//實質上是找第pos+1個數的位置在哪
{
int u=root;
push_down(u);
while(T[T[u].son[0]].Size!=pos)
{
if(pos<T[T[u].son[0]].Size)
u=T[u].son[0];
else
pos-=T[T[u].son[0]].Size+1,u=T[u].son[1];
push_down(u);
}
return u;
}
void reverse(int l,int r)//反轉區間
{
int u=Select(l-1),v=Select(r+1);
splay(u,0);
splay(v,u);
T[T[v].son[0]].rev^=1;
}
int build_tree(int l,int r)//建樹
{
if(l>r) return 0;
if(l==r) return l;
int mid=(l+r)>>1,sl,sr;
sl=T[mid].son[0]=build_tree(l,mid-1);
sr=T[mid].son[1]=build_tree(mid+1,r);
T[sl].fa=T[sr].fa=mid;
push_up(mid);
return mid;
}
void update(int l,int r,int val)//更新
{
int u=Select(l-1),v=Select(r+1);
splay(u,0);
splay(v,u);
T[T[v].son[0]].Max+=val;
T[T[v].son[0]].val+=val;
T[T[v].son[0]].lazy+=val;
}
int query(int l,int r)//查詢
{
int u=Select(l-1),v=Select(r+1);
splay(u,0);
splay(v,u);
return T[T[v].son[0]].Max;
}
void init(int n)
{
T[0].init(-inf),T[1].init(-inf),T[n+2].init(-inf);
for(int i=2; i<=n+1; i++) T[i].init(0);
root=build_tree(1,n+2),T[root].fa=0;
T[0].fa=0,T[0].son[1]=root,T[0].Size=0;
}
} ac;
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
ac.init(n);
while(m--)
{
int opt;
scanf("%d",&opt);
if(opt==1)
{
int l,r,val;
scanf("%d%d%d",&l,&r,&val);
ac.update(l,r,val);
}
else if(opt==2)
{
int l,r;
scanf("%d%d",&l,&r);
ac.reverse(l,r);
}
else if(opt==3)
{
int l,r;
scanf("%d%d",&l,&r);
printf("%d\n",ac.query(l,r));
}
}
}
return 0;
}
bzoj 1208 寵物收養所
這題可以用set寫代碼非常短,但是我在學習splay還是用splay寫了一發,結果無限TLE,最後發現大佬的板子還是不能亂改啊。
set版本
#include<set>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int mod=1e6;
const int inf=0x3f3f3f3f;
int main()
{
int n;
scanf("%d",&n);
set<int> s;
int flag,sum=0,ans=0;
s.insert(-inf);
s.insert(inf);
while(n--)
{
int opt,val;
scanf("%d%d",&opt,&val);
set<int>::iterator it;
if(sum==0)
{
flag=opt;
s.insert(val);
sum++;
}
else if(opt==flag)
{
s.insert(val);
sum++;
}
else
{
sum--;
it=s.lower_bound(val);
int tmp1=*it;
--it;
int tmp2=*it;
if(abs(val-tmp1)<abs(val-tmp2))
ans=(ans+abs(val-tmp1))%mod,s.erase(++it);
else
ans=(ans+abs(val-tmp2))%mod,s.erase(it);
}
}
printf("%d\n",ans);
}
splay版本
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ls(n) T[(n)].son[0]
#define rs(n) T[(n)].son[1]
#define LL long long
using namespace std;
const int maxn=1e6+10;
const LL inf=0x7fffffff;
const int mod=1e6;
int cnt;
struct splay_tree
{
int root,cont;
struct zp
{
int son[2];
LL val;
void init(LL _val)
{
val=_val;
son[0]=son[1]=0;
}
} T[maxn];
int fa[maxn];
void init()
{
cont=1;
root=0;
memset(fa,0,sizeof(fa));
}
int newnode(LL val)
{
T[cont].init(val);
cont++;
return cont-1;
}
void rotate(int x,int d)//0 左旋 1右旋
{
int y=fa[x],z=fa[y];
T[y].son[!d]=T[x].son[d],fa[T[x].son[d]]=y;
fa[y]=x,T[x].son[d]=y,fa[x]=z;
T[z].son[rs(z)==y]=x;
}
void splay(int x,int goal)//splay操作
{
if(x==0) return ;
while(fa[x]!=goal)
{
int y=fa[x],z=fa[y];
int rx=ls(y)==x,ry=ls(z)==y;
if(z==goal) rotate(x,rx);
else
{
if(rx==ry) rotate(y,ry);
else rotate(x,rx);
rotate(x,ry);
}
}
if(goal==0) root=x;
}
void insert(int &x,LL val,int pre)//插入操作
{
if(x==0)
{
x=newnode(val);
fa[x]=pre;
splay(x,0);
return ;
}
if(val<=T[x].val)
insert(ls(x),val,x);
else
insert(rs(x),val,x);
}
void del(int k,LL val)//刪除值爲val 的節點
{
if(T[k].val==val)//找到節點
{
splay(k,0);//先將該節點旋轉到根節點
int tmp=ls(k);
if(tmp==0)//左節點爲空
{
fa[rs(k)]=0;
root=rs(0)=rs(k);
return ;
}
while(rs(tmp)!=0)//找到左子樹中最大的值的節點
{
tmp=rs(tmp);
}
splay(tmp,k);//將這個節點旋轉到根節點的下方
root=tmp;//然後刪除根節點
rs(tmp)=rs(k);
fa[rs(k)]=tmp;
fa[tmp]=0;
rs(0)=tmp;
return ;
}
if(val<T[k].val)//遞歸找節點
return del(ls(k),val);
else
return del(rs(k),val);
}
LL query(int k,LL val,LL ans)//查詢最接近val值的節點並返回差值,ans代表到目前爲止最優的解
{
if(k==0)//找到最後了代表ans是這棵樹中最優的節點了
{
del(root,ans);
return abs(ans-val);
}
if(T[k].val==val)//找到相同的節點
{
del(root,val);
return 0;
}
if(abs(T[k].val-val)<abs(ans-val))//更新ans值
ans=T[k].val;
else if(abs(T[k].val-val)==abs(ans-val))
{
if(ans-val>0)
ans=T[k].val;
}
if(val<T[k].val)
return query(ls(k),val,ans);
else
return query(rs(k),val,ans);
}
} ac;
int main()
{
int n;
while(~scanf("%d",&n))
{
ac.init();
int flag=-1,sum=0;//flag標記這棵樹是什麼樹,0代表寵物 1代表領養者
LL ans=0;
while(n--)
{
cnt=0;
int opt;
LL val;
scanf("%d%lld",&opt,&val);
if(sum==0)//樹爲空
{
ac.root=0;
flag=opt;
ac.insert(ac.root,val,0);
sum++;
}
else if(opt==flag)
{
ac.insert(ac.root,val,0);
sum++;
}
else
{
ans=(ans+ac.query(ac.root,val,-inf))%mod;
sum--;
}
}
printf("%lld\n",ans);
}
}