郁闷的出纳员——动态统计(Treap,ST,Splay,BIT)
来源:互联网 发布:随身淘宝去异界书包网 编辑:程序博客网 时间:2024/06/02 02:21
郁闷的出纳员
64-bit integer IO format: %lld Java class name: Main
OIER公司是一家大型专业化软件公司,有着数以万计的员工。作为一名出纳员,我的任务之一便是统计每位员工的工资。这本来是一份不错的工作,但是令人郁闷的是,我们的老板反复无常,经常调整员工的工资。如果他心情好,就可能把每位员工的工资加上一个相同的量。反之,如果心情不好,就可能把他们的工资扣除一个相同的量。我真不知道除了调工资他还做什么其它事情。 工资的频繁调整很让员工反感,尤其是集体扣除工资的时候,一旦某位员工发现自己的工资已经低于了合同规定的工资下界,他就会立刻气愤地离开公司,并且再也不会回来了。每位员工的工资下界都是统一规定的。每当一个人离开公司,我就要从电脑中把他的工资档案删去,同样,每当公司招聘了一位新员工,我就得为他新建一个工资档案。 老板经常到我这边来询问工资情况,他并不问具体某位员工的工资情况,而是问现在工资第k多的员工拿多少工资。每当这时,我就不得不对数万个员工进行一次漫长的排序,然后告诉他答案。 好了,现在你已经对我的工作了解不少了。正如你猜的那样,我想请你编一个工资统计程序。怎么样,不是很困难吧?
Input
第一行有两个非负整数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是一个正整数。
在初始时,可以认为公司里一个员工也没有。
Output
输出文件的行数为F命令的条数加一。 对于每条F命令,你的程序要输出一行,仅包含一个整数,为当前工资第k多的员工所拿的工资数,如果k大于目前员工的数目,则输出-1。 输出文件的最后一行包含一个整数,为离开公司的员工的总数。
Sample Input
9 10
I 60
I 70
S 50
F 2
I 30
S 15
A 5
F 1
F 2
Sample Output
10
20
-1
2
Hint
I命令的条数不超过100000
A命令和S命令的总条数不超过100
F命令的条数不超过100000
每次工资调整的调整量不超过1000
新员工的工资不超过100000
Source
——————————————————————————————————————————————————
文档的分析:
与一般的修改不同,这道题要求对所有人修改,如果一个一个进行的话,修改工资的时间复杂度高达O(N)。如果我们反过来考虑,定义一个“基准值”,把所有人的工资看作“相对工资”,就是相对于基准值。这样每次修改所有人工资仅仅需要修改基准值就行了。于是变成了一个动态统计问题,建立一个Treap,存储相对工资。
为了方便考虑,定义基准值为delta,相对工资V对应的实际工资为F[V],则有F[V]=V+delta,V=F[V]-delta。定义工资下限为lowbound这是一个实际的下限,存储相对下限就是lowbound-delta。
对于I_k插入一个新的工资记录值k,k为实际工资,对应的相对工资为k-delta,应把k-delta插入Treap。
对于A_k,将基准值delta增加k。对于S_k,将基准值delta减少k,然后在Treap中删除所有小于(lowbound-delta)的元素。
由于我们总是查询第k多的工资,我们可以依照例1的方法,求(总数-k+1)小的工资。我们也不妨换种思路,把Treap建立成一个关键字反序大小比较的Treap,即在比较函数中规定如果a>b(实际的),则a小于b(逻辑的),a放在b的左子树。这虽然难以理解,但却能够满足一定的逻辑顺序。这样建立出的Treap就是自然的从大到小排序的了,查询第k多的工资,就是求排名第k的元素。
Treap指针的写法RE,然后WA。突然想起来大白上面说的:
在实际编码中,为了减少出错的可能性,一般用一个真实的指针null代替空指针NULL
Node *null=new Node()
#include<iostream>#include<cstdio>#include<cstdlib>#include<ctime>#define REP(i,n) for(int i=0;i<(n);++i)#define FOR(i,a,b) for(int i=(a);i<(b);++i)#define FORD(i,a,b) for(int i=(a);i<=(b);++i)using namespace std;int delta,n,m,leave;struct Treap { struct Node { int v,r,s;//相对工资,优先级,结点数 Node* ch[2]; int cmp(int x)const { if(x==v) return -1; return x<v?0:1; } void push_up() { s=ch[0]->s+ch[1]->s+1; } }; Node* root,*null; Treap() { null=new Node(); root=null; } void rotate(Node* &o,int d) //旋转 { Node* k=o->ch[d^1]; o->ch[d^1]=k->ch[d]; k->ch[d]=o; o->push_up(); k->push_up(); o=k; } void insert(Node* &o,int x) //插入 { if(o==null) { o=new Node(); o->ch[0]=o->ch[1]=null; o->v=x; o->s=1; o->r=rand(); } else { int d=(o->v>x?0:1); insert(o->ch[d],x); if(o->ch[d]->r > o->r) rotate(o,d^1); } o->push_up(); } int del(Node* &o,int x) { if(o==null) return 0; if(o->v<x) { int t=o->ch[0]->s+1; o=o->ch[1]; return t+del(o,x); } else { int t=del(o->ch[0],x); o->s-=t; return t; } } int find(Node* o,int k) //查找第k大 { if(o==null||k<0||k>o->s) return 0; int s=(o->ch[1]==null?0:o->ch[1]->s); if(k==s+1) return o->v; if(k<=s) return find(o->ch[1],k); else return find(o->ch[0],k-s-1); }} tp;int main(){//#ifndef ONLINE_JUDGE//freopen("in.cpp","r",stdin);//#endif // ONLINE_JUDGE tp=Treap(); delta=leave=0; scanf("%d%d",&n,&m); char op[10]; int x; while(n--) { scanf("%s%d",op,&x); if(op[0]=='I') if(x>=m) tp.insert(tp.root,x-delta); if(op[0]=='A') delta+=x; if(op[0]=='S') { delta-=x; leave+=tp.del(tp.root,m-delta); } if(op[0]=='F') { if(x> tp.root->s) printf("-1\n"); else printf("%d\n",tp.find(tp.root,x)+delta); } } printf("%d\n",leave); return 0;}
伸展树:插入时将插入的点伸展到根,使得在伸展的过程中使树趋于平衡,不容易被卡,而不是用push_up,虽然两者都能更新父节点的域。
#include<iostream>#include<cstdio>using namespace std;const int maxn=100010;int n,m;int delta,leave;struct Splay{ int ch[maxn][2],key[maxn],s[maxn],pre[maxn]; int root,tot; Splay(){root=tot=ch[0][0]=ch[1][0]=s[0]=pre[0]=key[0]=0;} void push_up(int rt){ int lson=ch[rt][0],rson=ch[rt][1]; s[rt]=s[lson]+s[rson]+1; } void rotate(int x,int d){ int y=pre[x]; ch[y][d^1]=ch[x][d]; pre[ch[x][d]]=y; if(pre[y]) ch[pre[y]][ch[pre[y]][1]==y]=x; pre[x]=pre[y]; ch[x][d]=y; pre[y]=x; push_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]; int d=ch[pre[y]][0]==y; if(ch[y][d]==x){ rotate(x,!d); rotate(x,d); }else{ rotate(y,d); rotate(x,d); } } } push_up(x); if(goal==0) root=x; } void insert(int &rt,int x,int f){ if(!rt){ rt=++tot; ch[rt][0]=ch[rt][1]=0; s[rt]=1; key[rt]=x; pre[rt]=f; splay(rt,0);//伸展 return; } if(key[rt]>x){ insert(ch[rt][0],x,rt); }else{ insert(ch[rt][1],x,rt); }// push_up(rt);会超时 } int del(int &rt,int x,int f){ if(!rt) return 0; int t; if(key[rt]<x){ t=s[ch[rt][0]]+1+del(ch[rt][1],x,rt); s[ch[rt][1]]=s[rt]-t; rt=ch[rt][1]; pre[rt]=f; }else{ t=del(ch[rt][0],x,rt); s[rt]-=t; } return t; } int find(int rt,int k){ int t=s[ch[rt][1]]; if(t+1==k) return key[rt]; if(k<=t) return find(ch[rt][1],k); else return find(ch[rt][0],k-t-1); } void pri(int rt){ if(!rt) return; pri(ch[rt][0]); printf("%d ",key[rt]); pri(ch[rt][1]); }};int main(){//#ifndef ONLINE_JUDGE//freopen("in.cpp","r",stdin);//#endif // ONLINE_JUDGE Splay spt=Splay(); scanf("%d%d",&n,&m); char op[10];int x; while(n--){ scanf("%s%d",op,&x); if(op[0]=='I') if(x>=m) {spt.insert(spt.root,x-delta,0);// spt.pri(spt.root);cout<<endl<<"size[root]="<<spt.s[spt.root]<<endl; } if(op[0]=='A') delta+=x; if(op[0]=='S'){ delta-=x; leave+=spt.del(spt.root,m-delta,0);// spt.pri(spt.root);// cout<<endl<<"size[root]="<<spt.s[spt.root]<<endl; } if(op[0]=='F'){ if(spt.s[spt.root]<x) printf("-1\n"); else printf("%d\n",spt.find(spt.root,x)+delta); } } printf("%d\n",leave); return 0;}
树状数组写法是类似的,用二分查找第k大,另外用一个优先队列来存放插入的点
#include<iostream>#include<cstdio>#include<queue>#include<vector>#include<functional>#include<cstring>const int inc=10000,maxn=200000;//我觉得按照题目的尿性,inc开100000好点吧?但是就WA了?数组越界?不可能,想不通using namespace std;int n,m;int delta,leave;int c[maxn+10];void update(int x,int v){ for(int i=x;i<=maxn;i+=i&-i){ c[i]+=v; }}int get_sum(int x){ int sum=0; for(int i=x;i>0;i-=i&-i){ sum+=c[i]; } return sum;}int findk(int k){ int l=1,r=maxn; while(l<r){ int m=(l+r)>>1; int t=get_sum(m); if(k<=t) r=m; else l=m+1; } return l+delta-inc;}int main(){//#ifndef ONLINE_JUDGE//freopen("in.cpp","r",stdin);//#endif // ONLINE_JUDGE memset(c,0,sizeof(c)); priority_queue<int,vector<int>,greater<int> > q; scanf("%d%d",&n,&m); char op[10];int x; while(n--){ scanf("%s%d",op,&x); if(op[0]=='I') if(x>=m){update(x-delta+inc,1);q.push(x-delta); } if(op[0]=='A') delta+=x; if(op[0]=='S'){ delta-=x; while(!q.empty()){ int u=q.top(); if(u>=m-delta) break; q.pop(); update(u+inc,-1); leave++; } } if(op[0]=='F'){ printf("%d\n",x<=q.size()?findk(q.size()-x+1):-1); } } printf("%d\n",leave); return 0;}
- 郁闷的出纳员——动态统计(Treap,ST,Splay,BIT)
- 郁闷的出纳员(treap)
- 郁闷的出纳员(splay)
- 郁闷的出纳员 Treap
- 【Splay|Treap】poj3580 SuperMemo && bzoj1503 [noi2004]郁闷的出纳员
- 郁闷的出纳员 ( splay )
- 平衡树(splay treap)(普通平衡树,郁闷的出纳员)
- BZOJ 1503 郁闷的出纳员 二叉平衡树(Treap,Splay)
- bzoj 1503 [NOI2004]郁闷的出纳员 平衡树(treap/Splay)
- HYSBZ - 1503 郁闷的出纳员 (treap)
- [NOI2004]郁闷的出纳员(Treap)
- 【NOI2004】郁闷的出纳员(splay)
- bzoj1503 [NOI2004]郁闷的出纳员(splay)
- 郁闷的出纳员 treap模板
- [NOI2004]郁闷的出纳员 (Splay)
- bzoj1503郁闷的出纳员 Splay
- Tyvj P1851 [NOI2004]郁闷的出纳员(Treap)
- [Vijos P1507][NOI 2004]郁闷的出纳员(Treap)
- 笔记整理(java基础六-----异常)
- 【译】resin4.0 API——resin.xml
- WPA/WPA2安全认证加密原理详解
- 【iOS 开发】Xcode6中计算文字的宽度或高度 ——伊禾媛
- 关于VS2010,C#环境下无法调整到Release模型下的解决方案
- 郁闷的出纳员——动态统计(Treap,ST,Splay,BIT)
- 笔记整理(java基础七-----问题整理)
- 三大WEB服务器对比分析(apache ,lighttpd,nginx)
- 笔记整理(java基础八-----多线程)
- 二维码的分类
- 构建Java并发模型框架
- 【Android】 FragmentTabHost+Fragment实现多标签页
- 计算未来:年轻程序猿如何凑够540万供老年花费?
- IOS利用fiddler抓取https请求