【题解】【bzoj 1503】【NOI2004】郁闷的出纳员
总体思路
首先,I 和 F 看起来比较清真,应该随便来个平衡树或者线段树之类的瞎维护一下就好。
主要是 A 和 S。
算法 1:
暴力加。
算法 2:
仿照线段树,打 Tag.
我们维护一个 Tag
变量和一个 minwage
变量。
别问我为什么是 wage,我也不知道。
I 操作的时候,要把给定的工资减去 Tag
。
F 操作的时候,找到之后加上 Tag
再输出即可。
A 操作的时候,设输入的第二个数为 delt
,则 Tag+=delt,minwage-=delt;
即可。
S 操作同理 A,只不过要处理一下工资过低的人们大规模 sudo rm -rf /
加跑路的情况。
我们用无旋 treap 维护这个过程,除了大规模跑路的情况以外,所有操作都应该已经很简单了。
至于删除所有工资低于给定数的人,在无旋 treap 上其实也就很简单了,把整棵树 split 成两颗(无旋 treap 基本操作),然后乱搞一下就好了(见代码)。
事实上啰嗦这么半天感觉还不如直接上代码简单。
奇怪的点:立刻离开公司的人不计入最后一行的答案。
代码:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e7+9;
struct node{
int val;
int rnd;
int l,r;
int sz;
}tree[N];
int n;
int tot,root;
int Tag,leavetot;
int minwage;
int newnode(int x){
tree[++tot]=(node){x,rand(),0,0,1};
return tot;
}
void pushup(int x){
tree[x].sz=tree[tree[x].l].sz+tree[tree[x].r].sz+1;
}
void split(int now,int k,int&l,int&r){
if(!now){
l=r=0;
return;
}
if(tree[now].val<=k){
l=now;
split(tree[now].r,k,tree[now].r,r);
}
else{
r=now;
split(tree[now].l,k,l,tree[now].l);
}
pushup(now);
}
int merge(int x,int y){
if(!x)return y;
if(!y)return x;
if(tree[x].rnd<tree[y].rnd){
tree[x].r=merge(tree[x].r,y);
return pushup(x),x;
}
else{
tree[y].l=merge(x,tree[y].l);
return pushup(y),y;
}
}
void insert(int val){
int x,y;
split(root,val,x,y);
root=merge(merge(x,newnode(val)),y);
}
int getval(int rk,int pos=root){
while(1){
int v=tree[tree[pos].l].sz;
if(rk==v+1)return tree[pos].val;
else if(rk<=v)pos=tree[pos].l;
else rk-=v+1,pos=tree[pos].r;
}
}
signed main(){
srand(time(0));
cin>>n>>minwage;
while(n--){
char op;cin>>op;
if(op=='I'){
int wage;cin>>wage;
wage-=Tag;
if(wage>=minwage)insert(wage);
}
else if(op=='A'){
int delt;cin>>delt;
Tag+=delt,minwage-=delt;
}
else if(op=='S'){
int delt;cin>>delt;
Tag-=delt,minwage+=delt;
int x,y;
split(root,minwage-1,x,y);
leavetot+=tree[x].sz;
root=y;
}
else{
int k;cin>>k;
if(k>tree[root].sz)cout<<-1<<endl;
else cout<<getval(tree[root].sz-k+1)+Tag<<endl;
}
}cout<<leavetot<<endl;
return 0;
}
不懂的欢迎留言 AWA.
Over.