bzoj 1503 郁闷的出纳员 (平衡树+前缀和)

题意

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是一个正整数。
在初始时,可以认为公司里一个员工也没有。
I命令的条数不超过100000
A命令和S命令的总条数不超过100
F命令的条数不超过100000
每次工资调整的调整量不超过1000
新员工的工资不超过100000

output

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

思路

准备用无旋treap练练手…
基本只要满足插入操作,和查询第K大就行。一个难点在于,每次修改权值对整体操作,同时要动态的查看当前有多少比 low小,然后整个删掉。可以按时间顺序维护一个sum,表示当前每个人的增量,每次sum变动之后 change 就是查找比 low-sum 小的个数。如果要新插入一个数字,除了特判当前和low的关系决定插不插入以外,之前的sum是不对其进行影响的,但是每次查完第 k 的要加上增量,因此在加入treap的时候先减去 sum,这样是符合要求的,不必担心 减完之后小于low怎么办,因为 low的基准也是 减去增量 sum的,所以相当于等式两边同时减掉sum。
然后代码方面我还是写的 siz_split ,这样会多比 val_split多一个getrank的时间,较之前对split的新理解就是,通过merge的左右关系,保证了权值/序列的有序性,val_split可以用siz_split结合getrank代替。

代码

贴一下FHQ_treap 板子。

#include<bits/stdc++.h>
using namespace std;
#define maxn 300005
#define maxm 1006
#define ll long long int
#define INF 0x3f3f3f3f
#define inc(i,l,r) for(int i=l;i<=r;i++)
#define dec(i,r,l) for(int i=r;i>=l;i--)
#define mem(a) memset(a,0,sizeof(a))
#define sqr(x) (x*x)
#define inf (ll)2e18+1
#define PI acos(-1)
#define mod 10007
#define auto(i,x) for(int i=head[x];i;i=ed[i].nxt)
ll read(){
    ll x=0,f=1ll;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
     while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
     return f*x;
}
int n,low;
int val[maxn],rnd[maxn],siz[maxn],ls[maxn],rs[maxn],tot,rt,sum,lef;
void pushup(int x){siz[x]=siz[ls[x]]+siz[rs[x]]+1; }
void split(int now,int k,int &x,int &y){
    if(now==0){x=y=0;return ;}
    if(k<=siz[ls[now]]){
        y=now;
        split(ls[now],k,x,ls[now]);
    }
    else {
        x=now;
        split(rs[now],k-siz[ls[now]]-1,rs[now],y);
    }
    pushup(now);
}
void Merge(int &now,int x,int y){
    if(!x||!y){now=x+y;return ;}
    if(rnd[x]<rnd[y]){
        now=x;
        Merge(rs[now],rs[now],y);
    }
    else{
        now=y;
        Merge(ls[now],x,ls[now]);
    }
    pushup(now);
}
int getrank(int va){
    int res=0,mi=INF,now=rt;
    while(now){
        if(va<=val[now]){
            if(va==val[now])mi=min(mi,res+siz[ls[now]]);
            now=ls[now];
        }
        else{
            res+=siz[ls[now]]+1;
            now=rs[now];
        }
    }
    return mi==INF?res:mi;
}
void insert(int va){
    if(va<low)return ;
    va-=sum;///
    int pos=getrank(va);
    int x=0,y=0,z=0;
    split(rt,pos,x,y);
    z=++tot;
    rnd[z]=rand();val[z]=va;siz[z]=1;ls[z]=rs[z]=0;
    Merge(rt,x,z);
    Merge(rt,rt,y);
}
void change(int va){
    //if(va>=0)return ;
    ///这里不能因为sum>=0就不修改  因为可能有刚刚插进来的  然后sum--了 于是low-sum 的标准提高了
    ///例如 sum=10 low=10
    ///I 10 
    ///S 1
    int pos=getrank(low-va);
    int x=0,y=0,z=0;
    split(rt,pos,x,z);
    rt=z;lef+=siz[x];
}
int getnum(int pos){
    if(pos>siz[rt])return -1;
    pos=siz[rt]-pos+1;
    int x=0,y=0,z=0;
    split(rt,pos,x,z);
    split(x,pos-1,x,y);
    int res=val[y];
    Merge(rt,x,y);
    Merge(rt,rt,z);
    return res;
}
void dfs(int x){
    if(val[x]==0){printf("*\n");return ; }
    printf("val = %d    siz = %d\n",val[x],siz[x]);
    dfs(ls[x]);
    dfs(rs[x]);
    printf("val = %d    siz = %d\n",val[x],siz[x]);
}
int main()
{
    srand(234213);
    n=read();low=read();
    char s[5];
    int x;
    inc(i,1,n){
        scanf("%s",s);
        if(s[0]=='I'){
            x=read();
            insert(x);
        }
        else if(s[0]=='A'){
            x=read();
            sum+=x;
            change(sum);
        }
        else if(s[0]=='S'){
            x=read();
            sum-=x;
            change(sum);
        }
        else{
            x=read();
            if(x>siz[rt])printf("-1\n");
            else printf("%d\n",getnum(x)+sum);
        }
        //printf("sum = %d\n",sum);
        //dfs(rt);
    }
    printf("%d\n",lef);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值