【例题】【Splay】NKOJ1921郁闷的出纳员

    xiaoxiao2021-12-14  16

    NKOJ1921 郁闷的出纳员 时间限制 : 10000 MS 空间限制 : 65536 KB

    问题描述 OIER公司是一家大型专业化软件公司,有着数以万计的员工。作为一名出纳员,我的任务之一便是统计每位员工的工资。这本来是一份不错的工作,但是令人郁闷的是,我们的老板反复无常,经常调整员工的工资。如果他心情好,就可能把每位员工的工资加上一个相同的量。反之,如果心情不好,就可能把他们的工资扣除一个相同的量。我真不知道除了调工资他还做什么其它事情。    工资的频繁调整很让员工反感,尤其是集体扣除工资的时候,一旦某位员工发现自己的工资已经低于了合同规定的工资下界,他就会立刻气愤地离开公司,并且再也不会回来了。每位员工的工资下界都是统一规定的。每当一个人离开公司,我就要从电脑中把他的工资档案删去,同样,每当公司招聘了一位新员工,我就得为他新建一个工资档案。    老板经常到我这边来询问工资情况,他并不问具体某位员工的工资情况,而是问现在工资第k多的员工拿多少工资。每当这时,我就不得不对数万个员工进行一次漫长的排序,然后告诉他答案。    好了,现在你已经对我的工作了解不少了。正如你猜的那样,我想请你编一个工资统计程序。怎么样,不是很困难吧?

    输入格式 第一行有两个非负整数n和min。n表示下面有多少条命令,min表示工资下界。    接下来的n行,每行表示一条命令。命令可以是以下四种之一:

    I命令 I_k 新建一个工资档案,初始工资为k。如果某员工的初始工资低于工资下界,他将立刻离开公司。 A命令 A_k 把每位员工的工资加上k S命令 S_k 把每位员工的工资扣除k F命令 F_k 查询第k多的工资

    _(下划线)表示一个空格,I命令、A命令、S命令中的k是一个非负整数,F命令中的k是一个正整数。 在初始时,可以认为公司里一个员工也没有。

    输出格式 输出文件的行数为F命令的条数加一。 对于每条F命令,你的程序要输出一行,仅包含一个整数,为当前工资第k多的员工所拿的工资数,如果k大于目前员工的数目,则输出-1。 输出文件的最后一行包含一个整数,为离开公司的员工的总数。

    样例输入 9 10 I 60 I 70 S 50 F 2 I 30 S 15 A 5 F 1 F 2

    样例输出 10 20 -1 2

    提示 I命令的条数不超过100000 A命令和S命令的总条数不超过100 F命令的条数不超过100000 每次工资调整的调整量不超过1000 新员工的工资不超过100000

    来源 NOI2004

    思路: 设实际工资=存储的工资-del (1)、加人:特判后insert,好像“如果某员工的初始工资低于工资下界,他将立刻离开公司”并不算作离开公司 (2)、加工资:将del减小 (3)、减工资:将del增加。 删除操作:找到第一个刚刚超过界限的人做根,将根的左子改为0,更新根的size。注意所有人都离开时特殊处理 (4)、找第k大:直接找

    #include<cstdio> #include<iostream> using namespace std; const int need=100004; typedef int int_[need]; //............................................. inline void inc(char &d) { d=getchar();while(d==' '||d==10) d=getchar(); } inline void in_(int &d) { char t=getchar();bool mark=0; while(t<'0'||t>'9') {if(t=='-') mark=1;t=getchar();} for(d=0;!(t<'0'||t>'9');t=getchar()) d=(d<<1)+(d<<3)+t-'0';if(mark) d=-d; } inline void out_(int x) { if(x<0) {x=-x;putchar('-');} if(x>=10) out_(x/10); putchar(x%10+'0'); } //............................................. int k,n,del,ans; int root,tot; int_ fa,ls,rs,si,val; #define NBHB(x) si[x]=si[ls[x]]+si[rs[x]]+1 inline void sr(int x) { int y=fa[x],z=fa[y]; ls[z]==y ? ls[z]=x :rs[z]=x; fa[x]=z; ls[y]=rs[x]; fa[rs[x]]=y; rs[x]=y; fa[y]=x; NBHB(y),NBHB(x); } inline void sl(int x) { int y=fa[x],z=fa[y]; ls[z]==y ? ls[z]=x :rs[z]=x; fa[x]=z; rs[y]=ls[x]; fa[ls[x]]=y; ls[x]=y; fa[y]=x; NBHB(y),NBHB(x); } inline void splay(int x) { int y; while(y=fa[x]) { if(ls[y]==x) sr(x); else sl(x); } root=x; } inline void insert(int d) { if(root==0) { val[root=1]=d; si[1]=1; ls[1]=rs[1]=fa[1]=0; tot++; return ; } tot++; int p=root; while(true) { if(d<val[p]) if(ls[p]) p=ls[p]; else {ls[p]=tot;break;} else if(rs[p]) p=rs[p]; else {rs[p]=tot;break;} } si[tot]=1;fa[tot]=p;val[tot]=d; splay(tot); } //............................................. int d; int find(int p)//找第一个大于等于d的节点 { if(p==0) return -1;//上一个节点没有左右儿子,返回-1 if(val[p]<d) return find(rs[p]); if(val[p]>=d) { int kk=find(ls[p]); if(kk!=-1&&val[kk]>=d) return kk; else return p; } } void deal(int dd) { d=dd; int kk=find(root); if(kk==-1) { ans+=si[root]; root=0; } else { splay(kk); ans+=si[ls[kk]]; ls[kk]=0,si[kk]=si[rs[kk]]+1; } } int getk(int k) { if(k>si[root]) return -1; int p=root; while(true) { if(si[rs[p]]>=k) p=rs[p]; else if(si[rs[p]]==k-1) return val[p]; else {k-=si[rs[p]]+1;p=ls[p];} } } //............................................. int main() { scanf("%d%d",&n,&k); char c;int b; for(int i=1,kk;i<=n;i++) { inc(c);in_(b); if(c=='I') { if(b>=k)insert(b+del); } else if(c=='A') del-=b; else if(c=='S') { del+=b; deal(k+del); } else { kk=getk(b); out_(kk==-1 ? kk :kk-del),putchar(10); } } out_(ans); }
    转载请注明原文地址: https://ju.6miu.com/read-971741.html

    最新回复(0)