【NOI2004T1】郁闷的出纳员-平衡树入门题
题目:郁闷的出纳员
做法:了解平衡树这个数据结构的人应该一眼就能看出这个题目可以用平衡树做(平衡树的教程网上太多了,这里就不赘述了),用平衡树维护插入、删除、查找第k小(注意由于题目中询问第k大,就是查找第(目前员工数-k+1)小)的操作即可,再用一个数delta来记录工资的整体变化量,注意处理就可以了。
以下是本人代码:
Treap 树堆(2016.8.10):
#include <cstdio>
#include <cstdlib>
#include <ctime>
#include <iostream>
#include <algorithm>
using namespace std;
int m,n,k,delta=0,root=0,cnt=0,ans=0;
char op;
struct node
{
int son[2],size,pri,key;
void newnode()
{
son[0]=son[1]=0;
size=1;pri=rand();
}
}tree[100010];
void rotate(int &v,bool p)
{
int x=tree[v].son[!p];
tree[v].son[!p]=tree[x].son[p];
tree[x].son[p]=v;
tree[x].size=tree[v].size;
tree[v].size=tree[tree[v].son[0]].size+tree[tree[v].son[1]].size+1;
v=x;
}
void insert(int &v,int key)
{
if (v==0)
{
v=++cnt;
tree[v].key=key;
tree[v].newnode();
}
else
{
tree[v].size++;
bool p=key<tree[v].key;
insert(tree[v].son[!p],key);
if (tree[v].pri<tree[tree[v].son[!p]].pri) rotate(v,p);
}
}
int del(int &v,int key)
{
int t;
if (v==0) return 0;
if (tree[v].key<key)
{
t=tree[tree[v].son[0]].size+1;
v=tree[v].son[1];
return t+del(v,key);
}
else
{
t=del(tree[v].son[0],key);
tree[v].size-=t;
return t;
}
}
int get_Kth(int &v,int k)
{
int s=tree[tree[v].son[0]].size+1;
if (k<s) return get_Kth(tree[v].son[0],k);
if (k>s) return get_Kth(tree[v].son[1],k-s);
return tree[v].key;
}
int main()
{
srand(time(NULL));
scanf("%d%d",&n,&m);getchar();
while(n--)
{
scanf("%c%d",&op,&k);getchar();
if (op=='I')
{
if (k>=m) insert(root,k-delta);
}
if (op=='A') delta+=k;
if (op=='S')
{
delta-=k;
ans+=del(root,m-delta);
}
if (op=='F')
{
if (!root||k>tree[root].size) printf("-1\n");
else printf("%d\n",get_Kth(root,tree[root].size-k+1)+delta);
}
}
printf("%d\n",ans);
return 0;
}
Splay Tree 伸展树(2016.8.26):
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,m,root=0,top=0,delta=0,ans=0;
int ch[100010][2],pre[100010],siz[100010],val[100010],c[100010],k;
char op[20];
void up(int x)
{
siz[x]=c[x]+siz[ch[x][0]]+siz[ch[x][1]];
}
void rotate(int x,int f)
{
int y=pre[x];
ch[y][!f]=ch[x][f];
pre[ch[x][f]]=y;
pre[x]=pre[y];
if (pre[x]) ch[pre[y]][ch[pre[y]][1]==y]=x;
ch[x][f]=y;
pre[y]=x;
up(y);
}
void Splay(int x,int goal)
{
while(pre[x]!=goal)
{
if (pre[pre[x]]==goal) rotate(x,ch[pre[x]][0]==x);
else
{
int y=pre[x],z=pre[y];
int f=(ch[z][0]==y);
if (ch[y][f]==x) rotate(x,!f),rotate(x,f);
else rotate(y,f),rotate(x,f);
}
}
up(x);
if (goal==0) root=x;
}
void newnode(int &v,int key,int f)
{
v=++top;
val[v]=key;pre[v]=f;
ch[v][0]=ch[v][1]=0;
siz[v]=c[v]=1;
}
void insert(int &v,int key,int f)
{
if (!v)
{
newnode(v,key,f);
Splay(v,0);
return;
}
if (key==val[v])
{
c[v]++;siz[v]++;
Splay(v,0);
return;
}
insert(ch[v][key>val[v]],key,v);
up(v);
}
int get_Kth(int x,int k)
{
int s=siz[ch[x][0]]+c[x];
if (k<=s-c[x]) return get_Kth(ch[x][0],k);
else if (k>s) return get_Kth(ch[x][1],k-s);
else
{
Splay(x,0);
return val[x];
}
}
int getkey(int x,int key)
{
if (key<val[x]) return getkey(ch[x][0],key);
else if (key>val[x]) return getkey(ch[x][1],key);
else
{
Splay(x,0);
return x;
}
}
void rto(int k,int goal)
{
int x=root;
while(1)
{
if (k<=siz[ch[x][0]]) x=ch[x][0];
else if (k>siz[ch[x][0]]+c[x]) {k-=siz[ch[x][0]]+c[x];x=ch[x][1];}
else break;
}
Splay(x,goal);
}
void del_root()
{
int t=root;
if (ch[root][1])
{
root=ch[root][1];
rto(1,0);
ch[root][0]=ch[t][0];
if (ch[t][0]) pre[ch[t][0]]=root;
}
else root=ch[root][0];
pre[root]=0;
up(root);
}
void Delete(int key)
{
int node=getkey(root,key);
Splay(node,0);
c[root]--;
if (!c[root]) del_root();
}
int main()
{
scanf("%d%d",&n,&m);
ch[0][0]=ch[0][1]=pre[0]=siz[0]=c[0]=val[0]=0;
while(n--)
{
scanf("%s%d",op,&k);
if (op[0]=='I') {if (k>=m) insert(root,k-delta,0);}
if (op[0]=='A') delta+=k;
if (op[0]=='S')
{
delta-=k;
int Min;
while(siz[root]&&(Min=get_Kth(root,1))<m-delta)
{
Delete(Min);
ans++;
}
}
if (op[0]=='F')
{
if (siz[root]<k) printf("-1\n");
else printf("%d\n",get_Kth(root,siz[root]-k+1)+delta);
}
}
printf("%d",ans);
return 0;
}
(个人感觉这题用伸展树编程复杂度较高而且不如treap...也可能是我太渣了...毕竟伸展树有更加高能的作用...)