NKOJ3776 工资管理

20 篇文章 0 订阅
11 篇文章 0 订阅

NKOJ3776 工资管理

题目描述

何老板的公司有 n 名员工,编号 1 到 n。一开始所有员工的工资都是 0。根据何老板的心 情好坏,可能出现下列两种针对员工工资的操作:

1.U x y 改工资操作:何老板将第 x 号员工的工资改成了 y;

2.Z x y 减工资操作:何老板生气了,他想选出 x 个员工,并将他们的工资全都减去 1。

何老板想知道,他能否一口气进行 y 次这样的减工资操作。能输出 TAK,否则输出 NIE。注意, y 次操作中,每次选的 x 个员工不一定要相同,并且员工的工资不能为负。 对于每个减工资的操作,何老板只是在心里想想,口头上说说,吓唬吓唬大家,解解闷 气,他并不会真正执行,只想知道是否可行。即不会对任何人的工资进行缩减。

输入格式

第一行包含两个正整数 n,m,分别表示员工的人数和操作次数。 接下来 m 行,每行一个操作,形式如题面所述。

输出格式

包含若干行,对于每个减工资操作,若可行,输出 TAK,否则输出 NIE。

数据范围

对于 30%的数据: 1<=n,m<=1000 1 <= n , m <= 1000 对于 100%的数据: 1<=n,m<=200000,1<=x<=n 1 <= n , m <= 200000 , 1 <= x <= n ,在 U 操作中 0<=y<=109 0 <= y <= 10 9 ,在 Z 操作中 1<=y<=109 1 <= y <= 10 9

解法

首先,工资数s>=y的员工有x个的操作肯定TAK

也就是说,如果工资数s>=y的员工有k个,那么这k个员工肯定就不用考虑了,我们只需要考虑剩下的(x-k)个员工

如果这(x-k)个员工的工资总数s_tot<(x-k)*y,肯定NIE;而如果s_tot>=(x-k)*y,就一定可行。证明(反证法):假设s_tot>=(x-k)*y,且在进行某一次减工资时,找不到符合条件的x个员工(符合条件的员工:该员工的工资大于0;注意,该员工的工资原本是小于**y的),而原来有s_tot>=(x-k)*y,现在(x-k)变小,那么肯定y’就会比y更大,显然不符合之前的条件(该员工的工资原本小于**y)。所以得到:当小于y的员工的工资总数s_tot>=(x-k)*y,该减工资操作就一定可行。

考场上当然蒙一蒙就可以了。

所以现在只需要维护两个树状数组:

1.大于某一个工资的人数(Modify1和GetSum1)

2.小于某一个工资的工资总数(工资总数= ∑ 工 资 ∗ 人 数 )(Modify2和GetSum2)

代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#define ll long long
#define N 200010
using namespace std;
ll n,m,Tree1[N],Tree2[N],x[N],y[N],B[N],Id[N],sala[N];bool Mark[N];
void Modify1(ll x,ll k){for(ll i=x;i<N;i+=(i&-i))Tree1[i]+=k;}
void Modify2(ll x,ll k){for(ll i=x;i<N;i+=(i&-i))Tree2[i]+=k;}
ll Getsum1(ll x){
    ll Ans=0;
    for(ll i=x;i>0;i-=(i&-i))Ans+=Tree1[i];
    return Ans;
}
ll Getsum2(ll x){
    ll Ans=0;
    for(ll i=x;i>0;i-=(i&-i))Ans+=Tree2[i];
    return Ans;
}
int main(){
    scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=m;i++){
        char s[5];scanf("%s",s);
        scanf("%lld%lld",&x[i],&y[i]);B[i]=y[i];
        if(s[0]=='U')Mark[i]=true;
    }
    sort(B+1,B+m+1);
    for(ll i=1;i<=m;i++)Id[i]=lower_bound(B+1,B+m+1,y[i])-B;//离散化,当然也可以在用的时候再lower_bound,不过这样之前要unique一下
    for(ll i=1;i<=m;i++){
        if(Mark[i]){
            if(sala[x[i]]){Modify1(sala[x[i]],-1);Modify2(sala[x[i]],-B[sala[x[i]]]);}
            Modify1(Id[i],1);Modify2(Id[i],B[Id[i]]);
            sala[x[i]]=Id[i];
        }else{
            ll t=Getsum1(m)-Getsum1(Id[i]-1);
            if(t>=x[i])puts("TAK");
            else{if(Getsum2(Id[i]-1)>=(x[i]-t)*B[Id[i]])puts("TAK");else puts("NIE");}
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值