题目链接:http://codevs.cn/problem/1286/
题解:伸展树,一定要记住,任何被操作过的节点都要旋转到根,插入节点后要把该节点旋转到根,查找节点后也要把它旋转到根,删除节点也要先旋转到根再删除,如果不旋转到根,妥妥的超时。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 100010
int c[N][2],fa[N],v[N],num[N],sum[N];
int n,m,cnt,rt;
void pushup(int k)
{
sum[k]=sum[c[k][0]]+sum[c[k][1]]+num[k];
}
void rotate(int x,int &k)
{
int y=fa[x],z=fa[y],l,r;
if(c[y][0]==x) l=0;else l=1;r=l^1;
if(y==k) k=x;
else if(c[z][0]==y) c[z][0]=x;else c[z][1]=x;
fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
c[y][l]=c[x][r];c[x][r]=y;
pushup(y);pushup(x);
}
void splay(int x,int &k)
{
while(x!=k)
{
int y=fa[x],z=fa[y];
if(y!=k)
{
if(c[z][0]==y^c[y][0]==x) rotate(x,k);
else rotate(y,k);
}
else rotate(x,k);
}
}
void ins(int val,int &k,int last)
{
if(k==0)
{
k=++cnt;v[k]=val;fa[k]=last;num[k]=sum[k]=1;
splay(k,rt);
}
else if(v[k]==val)
{
++num[k];++sum[k];splay(k,rt);
}
else if(val<=v[k]) ++sum[k],ins(val,c[k][0],k);
else ++sum[k],ins(val,c[k][1],k);
}
int find(int k,int val)
{
if(k==0) return 0;
else if(v[k]+val>=m) return find(c[k][0],val);
else
{
int t=find(c[k][1],val);
if(t==0) t=k;
return t;
}
}
void del(int val)
{
int x=find(rt,val);
if(x==0) return;
splay(x,rt);
rt=c[x][1];fa[rt]=0;
}
int find_k(int k,int rank)
{
int l=c[k][0],r=c[k][1];
if(rank<=sum[r]) return find_k(r,rank);
else if(sum[r]+num[k]<rank) return find_k(l,rank-sum[r]-num[k]);
else
{
splay(k,rt);return v[k];
}
}
int read()
{
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x;
}
int main()
{
n=read();m=read();
int z=0,k,tot=0;
char ch;
for(int i=1;i<=n;++i)
{
for(ch=getchar();ch<'A'||'Z'<ch;ch=getchar());
k=read();
switch(ch)
{
case 'I':if(k>=m) ++tot,ins(k-z,rt,0);break;
case 'A':z+=k;break;
case 'S':z-=k;del(z);break;
case 'F':
if(k>sum[rt]) printf("-1\n");
else printf("%d\n",find_k(rt,k)+z);
break;
}
}
printf("%d\n",tot-sum[rt]);
return 0;
}
本文详细介绍了伸展树算法的应用及实现细节,通过一个具体的题目示例解释了伸展树如何进行节点插入、查找和删除操作,并强调了操作后必须将目标节点旋转至根的重要性。
354

被折叠的 条评论
为什么被折叠?



