COGS314. [NOI2004] 郁闷的出纳员
★★★ 输入文件:cashier.in 输出文件:cashier.out 简单对比
时间限制:1 s 内存限制:128 MB
【问题描述】
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
题解:
对于每次工资的加减,如果加工资,让工资的下线MIN减去这个数,如果减工资,让工资的下线MIN加上这个数,就是用一种相对运动的思想。
再记录一个delta变量,来处理新加入的员工。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<queue>
#include<vector>
#include<algorithm>
using namespace std;
typedef long long LL;
const LL maxn=;
char s[];
LL root,tot,N,MIN,sum,delta;
LL key[maxn],lc[maxn],rc[maxn],siz[maxn];
void r_rotate(LL &rt){
LL k=lc[rt];
lc[rt]=rc[k];
rc[k]=rt;
siz[k]=siz[rt];
siz[rt]=siz[lc[rt]]+siz[rc[rt]]+;
rt=k;
}
void l_rotate(LL &rt){
LL k=rc[rt];
rc[rt]=lc[k];
lc[k]=rt;
siz[k]=siz[rt];
siz[rt]=siz[lc[rt]]+siz[rc[rt]]+;
rt=k;
}
void Maintain(LL &rt,bool flag){
if(flag==false){
if(siz[lc[lc[rt]]]>siz[rc[rt]]) r_rotate(rt);
else if(siz[rc[lc[rt]]]>siz[rc[rt]]){
l_rotate(lc[rt]);
r_rotate(rt);
}
else return ;
}
else{
if(siz[rc[rc[rt]]]>siz[lc[rt]]) l_rotate(rt);
else if(siz[lc[rc[rt]]]>siz[lc[rt]]){
r_rotate(rc[rt]);
l_rotate(rt);
}
else return ;
}
Maintain(lc[rt],); Maintain(rc[rt],);
Maintain(rt,); Maintain(rt,);
}
void insert(LL &rt,LL v){
if(rt==){
rt=++tot;
siz[rt]=;
lc[rt]=rc[rt]=;
key[rt]=v;
return ;
}
siz[rt]++;
if(v<=key[rt]) insert(lc[rt],v);
else insert(rc[rt],v);
Maintain(rt,); Maintain(rt,);
}
LL Delete(LL &rt,LL v){
LL ans;
siz[rt]--;
if(v==key[rt]||(v<key[rt]&&lc[rt]==)||(v>key[rt]&&rc[rt]==)){
ans=key[rt];
if(lc[rt]==||rc[rt]==) rt=lc[rt]+rc[rt];
else key[rt]=Delete(lc[rt],key[rt]+);
return ans;
}
else if(v<key[rt]) ans=Delete(lc[rt],v);
else ans=Delete(rc[rt],v);
return ans;
}
LL select(LL &rt,LL k){
if(k==siz[lc[rt]]+) return key[rt];
if(k<=siz[lc[rt]]) return select(lc[rt],k);
else return select(rc[rt],k--siz[lc[rt]]);
}
bool find(LL &rt,LL v){
if(rt==) return false;
else if(v==key[rt]) return true;
else if(v<key[rt]) return find(lc[rt],v);
else if(v>key[rt]) return find(rc[rt],v);
}
LL pred(LL &rt,LL v){
if(rt==) return v;
if(v<=key[rt]) return pred(lc[rt],v);
else{
LL ans=pred(rc[rt],v);
if(ans==v) return key[rt];
return ans;
}
}
void Clear(){
for(;;){
LL k=pred(root,MIN);
if(find(root,k)==true&&k<MIN) Delete(root,k),sum++;
else break;
}
}
int main(){
scanf("%lld%lld",&N,&MIN);
for(LL i=,tmp;i<=N;i++){
scanf("%s%lld",s,&tmp);
if(s[]=='I'){
if(tmp-delta<MIN){
continue;
}
insert(root,tmp-delta);
}
else if(s[]=='A'){//增加工资
MIN-=tmp;//要求标准下降
delta+=tmp;
Clear();
}
else if(s[]=='S'){//减少工资
MIN+=tmp;//要求标准上升
delta-=tmp;
Clear();
}
else{
if(siz[root]<tmp) puts("-1");
else printf("%lld\n",select(root,siz[root]-tmp+)+delta);
}
}
printf("%lld\n",sum);
return ;
}
下面再来一发splay版的,但是splay的删除操作要十分小心,情况一定要考虑全面,还有边界情况,我跪了整整一天半。。。
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
typedef long long LL;
const LL maxn=;
LL key[maxn],lc[maxn],rc[maxn],fa[maxn],siz[maxn];
LL tot,root;
char s[];
LL N,MIN,sum,delta;
void update(LL x){
siz[x]=siz[lc[x]]++siz[rc[x]];
}
void r_rotate(LL x){
LL y=fa[x];
lc[y]=rc[x];
if(rc[x]!=) fa[rc[x]]=y;
fa[x]=fa[y];
if(y==lc[fa[y]]) lc[fa[y]]=x;
else rc[fa[y]]=x;
fa[y]=x; rc[x]=y;
update(x); update(y);
}
void l_rotate(LL x){
LL y=fa[x];
rc[y]=lc[x];
if(lc[x]!=) fa[lc[x]]=y;
fa[x]=fa[y];
if(y==lc[fa[y]]) lc[fa[y]]=x;
else rc[fa[y]]=x;
fa[y]=x; lc[x]=y;
update(x); update(y);
}
void splay(LL x,LL s){
LL p;
while(fa[x]!=s){
p=fa[x];
if(fa[p]==s){
if(x==lc[p]) r_rotate(x);
else l_rotate(x);
break;
}
if(x==lc[p]){
if(p==lc[fa[p]]) r_rotate(p),r_rotate(x);
else r_rotate(x),l_rotate(x);
}
else{
if(p==rc[fa[p]]) l_rotate(p),l_rotate(x);
else l_rotate(x),r_rotate(x);
}
}
if(s==) root=x;
update(x);
}
void New_node(LL &x,LL fath,LL v){//建立新节点
x=++tot;
lc[x]=rc[x]=; siz[x]=;
fa[x]=fath;
key[x]=v;
}
void insert(LL v){//插入新节点
if(root==){
New_node(root,,v);
return ;
}
LL p,x=root;
while(x!=){
p=x;
if(v<=key[x]) siz[x]++,x=lc[x];
else siz[x]++,x=rc[x];
}
if(v<=key[p]) New_node(lc[p],p,v);
else New_node(rc[p],p,v);
splay(tot,);
}
LL find(LL v){//查找在这棵树中键值为v的节点
LL x=root;
while(x!=){
if(v<key[x]) x=lc[x];
else if(v>key[x]) x=rc[x];
else if(v==key[x]){
splay(x,);
return x;
}
}
return -;
}
LL getmax(LL x){//找到以x为根的最大值
while(rc[x]!=) x=rc[x];
return x;
}
LL getmin(LL x){//找到以x为根的最小值
while(lc[x]!=) x=lc[x];
return x;
}
LL getpre(LL x){//找到节点x的前驱
splay(x,);
if(lc[x]==) return -;
return getmax(lc[x]);
}
LL getne(LL x){//找到节点x的后继
splay(x,);
if(rc[x]==) return -;
return getmin(rc[x]);
}
LL join(LL s1,LL s2){//把以s1和s2为根的子树合并,返回根
if(s1==||s2==){
if(s1==&&s2==){
rc[]=;
return ;
}
if(s1==){
rc[fa[s2]]=; rc[]=s2; fa[s2]=;
siz[root]=;
return s2;
}
else{
lc[fa[s1]]=; rc[]=s1; fa[s1]=;
siz[root]=;
return s1;
}
}
LL p=getmax(s1);//由getmax()函数可知,p要么是根的左孩子,要么是其父亲的右孩子, p肯定没有右孩子
if(p==lc[fa[p]]){//p是根的左孩子
if(lc[p]!=) //如果p有左孩子
lc[fa[p]]=lc[p],fa[lc[p]]=fa[p];//把p的左孩子接到父节点上
else lc[fa[p]]=;
}
else{//p是其父亲的右孩子
if(lc[p]!=) //如果p有左孩子
rc[fa[p]]=lc[p],fa[lc[p]]=fa[p];//把p的左孩子接到父节点上
else rc[fa[p]]=;
}
update(fa[p]);//更新p的父亲s的iz值
LL rt=fa[s1];
lc[rt]=rc[rt]=; siz[rt]=;//删去原根
if(s1!=p) lc[p]=s1,fa[s1]=p; //p的左孩子变成原根的左孩子
rc[p]=s2; fa[s2]=p; //p的左孩子变成原根的右孩子
fa[p]=; rc[]=p;
update(p);
return p;
}
void Delete(LL v){//删除一个键值为v的节点
LL x=find(v);
root=join(lc[x],rc[x]);
} LL findkth(LL x,LL k){//在以x为根的树中找第 k大
if(siz[lc[x]]+==k) return key[x];
if(siz[lc[x]]+>k) return findkth(lc[x],k);
return findkth(rc[x],k-siz[lc[x]]-);
} LL pred(LL rt,LL v){//返回比 v小的最大的数
if(rt==) return v;
if(v<=key[rt]) return pred(lc[rt],v);
else{
LL ans=pred(rc[rt],v);
if(ans==v) return key[rt];
return ans;
}
}
void Clear(){
for(;;){
LL k=pred(root,MIN);
if(find(k)!=-&&k<MIN){
Delete(k);
sum++;
}
else break;
}
}
int main(){
scanf("%lld%lld",&N,&MIN);
for(LL i=,tmp;i<=N;i++){
scanf("%s%lld",s,&tmp);
if(s[]=='I'){
if(tmp-delta<MIN){
continue;
}
insert(tmp-delta);
}
else if(s[]=='A'){//增加工资
MIN-=tmp;//要求标准下降
delta+=tmp;
Clear();
}
else if(s[]=='S'){//减少工资
MIN+=tmp;//要求标准上升
delta-=tmp;
Clear();
}
else{
if(siz[root]<tmp) puts("-1");
else printf("%lld\n",findkth(root,siz[root]-tmp+)+delta);
}
}
printf("%lld\n",sum);
return ;
}
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
typedef long long LL;
const LL maxn=;
LL key[maxn],lc[maxn],rc[maxn],fa[maxn],siz[maxn];
LL tot,root;
char s[];
LL N,MIN,sum,delta;
void update(LL x){
siz[x]=siz[lc[x]]++siz[rc[x]];
}
void r_rotate(LL x){
LL y=fa[x];
lc[y]=rc[x];
if(rc[x]!=) fa[rc[x]]=y;
fa[x]=fa[y];
if(y==lc[fa[y]]) lc[fa[y]]=x;
else rc[fa[y]]=x;
fa[y]=x; rc[x]=y;
update(x); update(y);
}
void l_rotate(LL x){
LL y=fa[x];
rc[y]=lc[x];
if(lc[x]!=) fa[lc[x]]=y;
fa[x]=fa[y];
if(y==lc[fa[y]]) lc[fa[y]]=x;
else rc[fa[y]]=x;
fa[y]=x; lc[x]=y;
update(x); update(y);
}
void splay(LL x,LL s){
LL p;
while(fa[x]!=s){
p=fa[x];
if(fa[p]==s){
if(x==lc[p]) r_rotate(x);
else l_rotate(x);
break;
}
if(x==lc[p]){
if(p==lc[fa[p]]) r_rotate(p),r_rotate(x);
else r_rotate(x),l_rotate(x);
}
else{
if(p==rc[fa[p]]) l_rotate(p),l_rotate(x);
else l_rotate(x),r_rotate(x);
}
}
if(s==) root=x;
update(x);
}
void New_node(LL &x,LL fath,LL v){//建立新节点
x=++tot;
lc[x]=rc[x]=; siz[x]=;
fa[x]=fath;
key[x]=v;
}
void insert(LL v){//插入新节点
if(root==){
New_node(root,,v);
return ;
}
LL p,x=root;
while(x!=){
p=x;
if(v<=key[x]) siz[x]++,x=lc[x];
else siz[x]++,x=rc[x];
}
if(v<=key[p]) New_node(lc[p],p,v);
else New_node(rc[p],p,v);
splay(tot,);
}
LL find(LL v){//查找在这棵树中键值为v的节点
LL x=root;
while(x!=){
if(v<key[x]) x=lc[x];
else if(v>key[x]) x=rc[x];
else if(v==key[x]){
splay(x,);
return x;
}
}
return -;
}
LL getmax(LL x){//找到以x为根的最大值
while(rc[x]!=) x=rc[x];
return x;
}
LL getmin(LL x){//找到以x为根的最小值
while(lc[x]!=) x=lc[x];
return x;
}
LL getpre(LL x){//找到节点x的前驱
splay(x,);
if(lc[x]==) return -;
return getmax(lc[x]);
}
LL getne(LL x){//找到节点x的后继
splay(x,);
if(rc[x]==) return -;
return getmin(rc[x]);
}
LL join(LL s1,LL s2){//把以s1和s2为根的子树合并,返回根
if(s1==||s2==){
if(s1==&&s2==){
rc[]=;
return ;
}
if(s1==){
rc[fa[s2]]=; rc[]=s2; fa[s2]=;
siz[root]=;
return s2;
}
else{
lc[fa[s1]]=; rc[]=s1; fa[s1]=;
siz[root]=;
return s1;
}
}
LL p=getmax(s1);//由getmax()函数可知,p要么是根的左孩子,要么是其父亲的右孩子, p肯定没有右孩子
if(p==lc[fa[p]]){//p是根的左孩子
if(lc[p]!=) //如果p有左孩子
lc[fa[p]]=lc[p],fa[lc[p]]=fa[p];//把p的左孩子接到父节点上
else lc[fa[p]]=;
}
else{//p是其父亲的右孩子
if(lc[p]!=) //如果p有左孩子
rc[fa[p]]=lc[p],fa[lc[p]]=fa[p];//把p的左孩子接到父节点上
else rc[fa[p]]=;
}
update(fa[p]);//更新p的父亲s的iz值
LL rt=fa[s1];
lc[rt]=rc[rt]=; siz[rt]=;//删去原根
if(s1!=p) lc[p]=s1,fa[s1]=p; //p的左孩子变成原根的左孩子
rc[p]=s2; fa[s2]=p; //p的左孩子变成原根的右孩子
fa[p]=; rc[]=p;
update(p);
return p;
}
void Delete(LL v){//删除一个键值为v的节点
LL x=find(v);
root=join(lc[x],rc[x]);
} LL findkth(LL x,LL k){//在以x为根的树中找第 k大
if(siz[lc[x]]+==k) return key[x];
if(siz[lc[x]]+>k) return findkth(lc[x],k);
return findkth(rc[x],k-siz[lc[x]]-);
} LL pred(LL rt,LL v){//返回比 v小的最大的数
if(rt==) return v;
if(v<=key[rt]) return pred(lc[rt],v);
else{
LL ans=pred(rc[rt],v);
if(ans==v) return key[rt];
return ans;
}
}
void Clear(){
for(;;){
LL k=pred(root,MIN);
if(find(k)!=-&&k<MIN){
Delete(k);
sum++;
}
else break;
}
}
int main(){
freopen("cashier.in","r",stdin);
freopen("cashier.out","w",stdout);
scanf("%lld%lld",&N,&MIN);
for(LL i=,tmp;i<=N;i++){
scanf("%s%lld",s,&tmp);
if(s[]=='I'){
if(tmp-delta<MIN){
continue;
}
insert(tmp-delta);
}
else if(s[]=='A'){//增加工资
MIN-=tmp;//要求标准下降
delta+=tmp;
Clear();
}
else if(s[]=='S'){//减少工资
MIN+=tmp;//要求标准上升
delta-=tmp;
Clear();
}
else{
if(siz[root]<tmp) puts("-1");
else printf("%lld\n",findkth(root,siz[root]-tmp+)+delta);
}
}
printf("%lld\n",sum);
return ;
}
COGS314. [NOI2004] 郁闷的出纳员的更多相关文章
- BZOJ 1503: [NOI2004]郁闷的出纳员
1503: [NOI2004]郁闷的出纳员 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 10526 Solved: 3685[Submit][Stat ...
- [BZOJ1503][NOI2004]郁闷的出纳员
[BZOJ1503][NOI2004]郁闷的出纳员 试题描述 OIER公司是一家大型专业化软件公司,有着数以万计的员工.作为一名出纳员,我的任务之一便是统计每位员工的工资.这本来是一份不错的工作,但是 ...
- BZOJ 1503: [NOI2004]郁闷的出纳员 splay
1503: [NOI2004]郁闷的出纳员 Description OIER公司是一家大型专业化软件公司,有着数以万计的员工.作为一名出纳员,我的任务之一便是统计每位员工的工资.这本来是一份不错的工作 ...
- bzoj 1503: [NOI2004]郁闷的出纳员 Treap
1503: [NOI2004]郁闷的出纳员 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 6263 Solved: 2190[Submit][Statu ...
- bzoj1503 [NOI2004]郁闷的出纳员(名次树+懒惰标记)
1503: [NOI2004]郁闷的出纳员 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 8705 Solved: 3027[Submit][Statu ...
- BZOJ_1503_[NOI2004]郁闷的出纳员_权值线段树
BZOJ_1503_[NOI2004]郁闷的出纳员_权值线段树 Description OIER公司是一家大型专业化软件公司,有着数以万计的员工.作为一名出纳员,我的任务之一便是统计每位员工的 工资. ...
- 1503: [NOI2004]郁闷的出纳员 (SBT)
1503: [NOI2004]郁闷的出纳员 http://www.lydsy.com/JudgeOnline/problem.php?id=1503 Time Limit: 5 Sec Memory ...
- P1486 [NOI2004]郁闷的出纳员
P1486 [NOI2004]郁闷的出纳员 题目描述 OIER公司是一家大型专业化软件公司,有着数以万计的员工.作为一名出纳员,我的任务之一便是统计每位员工的工资.这本来是一份不错的工作,但是令人郁闷 ...
- [BZOJ1503][NOI2004]郁闷的出纳员 无旋Treap
1503: [NOI2004]郁闷的出纳员 Time Limit: 5 Sec Memory Limit: 64 MB Description OIER公司是一家大型专业化软件公司,有着数以万计的员 ...
随机推荐
- UVALive 6933 Virus synthesis(回文树)
Viruses are usually bad for your health. How about ghting them with... other viruses? In this proble ...
- c#修改cpu主频
并不是真正能修改硬件,只是一个数据,能骗过部分程序检测,如英雄联盟必须达到3.0的主频才能使用录像功能,通过修改可以达到要求. 下面是代码: public enum RegValueKind { // ...
- Spark源码分析 – Checkpoint
CP的步骤 1. 首先如果RDD需要CP, 调用RDD.checkpoint()来mark 注释说了, 这个需要在Job被执行前被mark, 原因后面看, 并且最好选择persist这个RDD, 否则 ...
- Android技巧小结之新旧版本Notification
最近开发用到了通知功能,但有几个地方老是提示deprecated,然后就找了篇文章学习了下新旧版本的不同. Notification即通知,用于在通知栏显示提示信息. 在较新的版本中(API leve ...
- Cache与主存之间的全相联映射,直接映射和组相联映射的区别
2017-02-22 注:本文并非原创,来自百度文库,只是觉得写得较好,故分享之.若是某人的知识产权,望告知!谢谢 1.高速缓冲存储器的功能.结构与工作原理 高速缓冲存储器是存在于主存与CPU之间的一 ...
- LInux下桥接模式详解二
上篇文章导入博客园的比较早,而这篇自己在写的时候才发现内部复杂的很,以至于没能按时完成,造成两篇文章的间隔时间有点长! 话不多说,言归正传! 前面的文章介绍了桥接模式下的基础理论知识,其实本节想结合L ...
- IIS7.5 错误代码0x8007007e HTTP 错误 500.19
目录 问题案例 解决问题 参考资料 问题案例 今天在win2008+IIS7.5的环境中部署WCF服务后,一直出现无法打开的页面.具体错误信息如下: HTTP 错误 500.19 - Internal ...
- sql server数据库状态监控
sql server数据库监控 转自:https://www.cnblogs.com/seusoftware/category/500793.html 6. SQL Server数据库监控 - 如 ...
- 004-Shell 基本运算符、算术运算符、关系运算符、布尔运算符、辑运算符、字符串运算符、文件测试运算符
一.概述 Shell 和其他编程语言一样,支持多种运算符,包括: 算数运算符 关系运算符 布尔运算符 字符串运算符 文件测试运算符 二.算术运算符 原生bash不支持简单的数学运算,但是可以通过其他命 ...
- 练习webpack遇到的一些问题以及解决办法,供自己以后参考
1.利用nodeJs安装webpack时报出以下错误提示: 这个错误我在网上搜了一下,说是npm文件配置问题,也就是权限不够 解决办法:npm config set registry http://r ...