题目描述
题解
平衡树splay练习题。
因为懒得做区间修改,就加了一个可正可负的变量money,来表示老板修改工资的总变化量。然后使tree中val+money等于员工的实际工资。
因为涉及到区间的删除,要插入最大值和最小值INF和-INF。删除子树的操作要注意,刚开始写错了。
代码
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
const int max_n=2e5+5;
const int INF=2e9;
int n,minn,k;
int f[max_n],ch[max_n][2],size[max_n],cnt[max_n],key[max_n];
int root,sz;
int money,totinsert,totnow,ans;
inline void clear(int x){
f[x]=ch[x][0]=ch[x][1]=size[x]=cnt[x]=key[x]=0;
}
inline bool get(int x){
return ch[ f[x] ][1]==x;
}
inline void update(int x){
if (x){
size[x]=cnt[x];
if (ch[x][0]) size[x]+=size[ ch[x][0] ];
if (ch[x][1]) size[x]+=size[ ch[x][1] ];
}
}
inline void rotate(int x){
int old=f[x],oldf=f[old],which=get(x);
ch[old][which]=ch[x][which^1];
f[ ch[old][which] ]=old;
ch[x][which^1]=old;
f[old]=x;
f[x]=oldf;
if (oldf)
ch[oldf][ ch[oldf][1]==old ]=x;
update(old);
update(x);
}
inline void splay(int x,int tar){
for (int fa;(fa=f[x])!=tar;rotate(x))
if (f[fa]!=tar)
rotate( (get(x)==get(fa)) ?fa:x );
if (tar==0) root=x;
}
inline void insert(int x){
if (root==0){
root=++sz;
f[sz]=ch[sz][0]=ch[sz][1]=0;
size[sz]=cnt[sz]=1;
key[sz]=x;
return;
}
int now=root,fa=0;
while (1){
if (x==key[now]){
++cnt[now];
update(now);
splay(now,0);
break;
}
fa=now;
now=ch[now][ key[now]<x ];
if (now==0){
++sz;
f[sz]=fa;
ch[sz][0]=ch[sz][1]=0;
size[sz]=cnt[sz]=1;
key[sz]=x;
ch[fa][ key[fa]<x ]=sz;
update(sz);
update(fa);
splay(sz,0);
break;
}
}
}
//查询tree中val为x的点的编号
inline int findnumber(int x){
int now=root;
while (1){
if (x<key[now])
now=ch[now][0];
else{
if (x==key[now]){
splay(now,0);
return now;
}
now=ch[now][1];
}
}
}
//查询第x小的点在tree中的val
inline int findval(int x){
int now=root;
while (1){
if (ch[now][0]&&x<=size[ ch[now][0] ])
now=ch[now][0];
else{
int temp=cnt[now];
if (ch[now][0])
temp+=size[ ch[now][0] ];
if (x<=temp) return key[now];
x-=temp;
now=ch[now][1];
}
}
}
//查询tree中val为x的点第几小
inline int findminn(int x){
int now=root,ans=0;
while (1){
if (x<key[now])
now=ch[now][0];
else{
if (ch[now][0])
ans+=size[ ch[now][0] ];
if (x==key[now]) return ans+1;
ans+=cnt[now];
now=ch[now][1];
}
}
}
inline int pre(){
int now=ch[root][0];
while (ch[now][1])
now=ch[now][1];
return now;
}
inline int next(){
int now=ch[root][1];
while (ch[now][0])
now=ch[now][0];
return now;
}
inline void del(int x){
int whatever=findnumber(x);
if (cnt[root]>1){
cnt[root]--;
update(root);
return;
}
if (!ch[root][0]&&!ch[root][1]){
clear(root);
root=0;
return;
}
if (!ch[root][0]){
int oldroot=root;
root=ch[oldroot][1];
f[root]=0;
clear(oldroot);
return;
}
if (!ch[root][1]){
int oldroot=root;
root=ch[oldroot][0];
f[root]=0;
clear(oldroot);
return;
}
int leftbig=pre(),oldroot=root;
splay(leftbig,0);
ch[root][1]=ch[oldroot][1];
f[ ch[root][1] ]=root;
clear(oldroot);
update(root);
}
int main(){
scanf("%d%d",&n,&minn);
money=0;
insert(INF);
insert(-INF);
for (int i=1;i<=n;++i){
char c=getchar();
while (c!='I'&&c!='S'&&c!='A'&&c!='F') c=getchar();
scanf("%d",&k);
if (c=='I'){
if (k<minn) continue;
insert(k-money);
totinsert++;
}
if (c=='A')
money+=k;
if (c=='S'){
money-=k;
insert(minn-money);
int aa=findnumber(-INF);
int bb=findnumber(minn-money);
splay(aa,0);
splay(bb,aa);
ch[ ch[root][1] ][0]=0;
del(minn-money);
}
if (c=='F'){
totnow=findminn(INF)-2;
if (totnow<k){
printf("-1\n");
continue;
}
ans=findval(totnow+2-k);
printf("%d\n",ans+money);
}
}
totnow=findminn(INF)-2;
ans=totinsert-totnow;
printf("%d\n",ans);
}
总结
平衡树的删除子树要注意。