刷题总结——郁闷的出纳员(bzoj1503)
题目:
题目背景
NOI2004 DAY1 T1
题目描述
OIER 公司是一家大型专业化软件公司,有着数以万计的员工。作为一名出纳员,我的任务之一便是统计每位员工的工资。这本来是一份不错的工作,但是令人郁闷的是,我们的老板反复无常,经常调整员工的工资。如果他心情好,就可能把每位员工的工资加上一个相同的量。反之,如果心情不好,就可能把他们的工资扣除一个相同的量。我真不知道除了调工资他还做什么其它事情。
工资的频繁调整很让员工反感,尤其是集体扣除工资的时候,一旦某位员工发现自己的工资已经低于了合同规定的工资下界,他就会立刻气愤地离开公司,并且再也不会回来了。每位员工的工资下界都是统一规定的。每当一个人离开公司,我就要从电脑中把他的工资档案删去,同样,每当公司招聘了一位新员工,我就得为他新建一个工资档案。
老板经常到我这边来询问工资情况,他并不问具体某位员工的工资情况,而是问现在工资第 k 多的员工拿多少工资。每当这时,我就不得不对数万个员工进行一次漫长的排序,然后告诉他答案。
好了,现在你已经对我的工作了解不少了。正如你猜的那样,我想请你编一个工资统计程序。怎么样,不是很困难吧?
输入格式
第一行有两个非负整数 n 和 min 。n 表示下面有多少条命令,min 表示工资下界。
接下来的 n 行,每行表示一条命令。命令可以是以下四种之一:
“下划线( _ )”表示一个空格,I 命令、A 命令、S 命令中的 k 是一个非负整数,F 命令中的 k 是一个正整数。
在初始时,可以认为公司里一个员工也没有。
输出格式
输出的行数为 F 命令的条数加一。
对于每条 F 命令,你的程序要输出一行,仅包含一个整数,为当前工资第 k 多的员工所拿的工资数,如果 k 大于目前员工的数目,则输出 -1 。
输出的最后一行包含一个整数,为离开公司的员工的总数。
样例数据 1
备注
【数据范围】
I 命令的条数不超过 100000
A 命令和 S 命令的总条数不超过 100
F 命令的条数不超过 100000
每次工资调整的调整量不超过 1000
新员工的工资不超过 100000
【评分方法】
对于每个测试点,如果你输出文件的行数不正确,或者输出文件中含有非法字符,得分为 0 。
否则你的得分按如下方法计算:
- 如果对于所有的 F 命令,你都输出了正确的答案,并且最后输出的离开公司的人数也是正确的,你将得到 10 分;
- 如果你只对所有的 F 命令输出了正确答案,得 6 分;
- 如果只有离开公司的人数是正确的,得 4 分;否则得 0 分。
题解:
重要的事情先说下:
仔细审题!!!!!
做的时候以为加入公司就走的员工是要算入总数的·····结果tm不算·····感觉题目也没说清楚啊···
其余的就是splay的基本操作了···另外这道题有个很巧妙的地方是记录一个sum维护修改公司的总和,并根据sum不是直接修改所有员工的工资数,而是修改加入的人的工资数和每次判断离开公司的人时修改minn(具体看代码)····,这样就能节约复杂度了···
代码:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<ctime> #include<cctype> #include<cstring> #include<string> #include<algorithm> using namespace std; const int N=3e5+5; int val[N],father[N],cnt[N],son[N][2],size[N],root,tot; int n,a,minn,totl=0,sum=0; long long totcut=0; char s[5]; inline int R() { char c;int f=0; for(c=getchar();c<'0'||c>'9';c=getchar()); for(;c<='9'&&c>='0';c=getchar()) f=(f<<3)+(f<<1)+c-'0'; return f; } inline void clear(int now) { size[now]=son[now][0]=son[now][1]=val[now]=cnt[now]=father[now]=0; } inline void update(int now) { if(now) { size[now]=cnt[now]; if(son[now][0]) size[now]+=size[son[now][0]]; if(son[now][1]) size[now]+=size[son[now][1]]; } } inline int get(int a) { return son[father[a]][1]==a; } inline void rotate(int now) { int fa=father[now],ofa=father[fa],which=get(now); son[fa][which]=son[now][which^1],father[son[fa][which]]=fa; son[now][which^1]=fa,father[fa]=now,father[now]=ofa; if(ofa) son[ofa][son[ofa][1]==fa]=now; update(fa),update(now); } inline void splay(int now) { while(father[now]) { if(father[father[now]]) rotate(get(now)==get(father[now])?father[now]:now); rotate(now); } root=now; } inline void insert(int x) { int now=root,last=0; while(true) { if(!now) { now=++tot;size[now]=cnt[now]=1;father[now]=last;val[now]=x; son[last][val[now]>val[last]]=now;update(last);splay(now); break; } if(val[now]==x) { cnt[now]++;update(now);update(last);splay(now); break; } last=now;now=son[now][x>val[now]]; } } inline int findx(int x) { int now=root; while(true) { if(x<=size[son[now][0]]) now=son[now][0]; else { int temp=size[son[now][0]]+cnt[now]; if(x<=temp) return val[now]; x-=temp;now=son[now][1]; } } } inline int Delete() { int temp=size[son[root][0]]+cnt[root];int oldroot=root; root=son[oldroot][1];father[root]=0; clear(oldroot); return temp; } int main() { //freopen("a.in","r",stdin); n=R(),minn=R(); while(n--) { scanf("%s%d",s,&a); if(s[0]=='I') { if(a-sum>(minn-sum-1)) insert(a-sum),totl++; } if(s[0]=='A') sum+=a; if(s[0]=='S') sum-=a; if(s[0]=='F') { if(totl<a) cout<<"-1"<<endl; else { int ans=findx(totl-a+1); cout<<ans+sum<<endl; } } int limit=minn-sum-1;insert(limit); int temp=Delete(); totcut+=(long long)(temp-1); totl=totl-temp+1; } cout<<totcut<<endl; return 0; }