问题描述
何老板的公司有n名员工,编号1到n。一开始所有员工的工资都是0。根据何老板的心情好坏,可能出现下列两种针对员工工资的操作:
1.U x y 改工资操作:何老板将第x号员工的工资改成了y;
2.Z x y 减工资操作:何老板生气了,他想选出x个员工,并将他们的工资全都减去1。何老板想知道,他能否一口气进行y次这样的减工资操作。能输出TAK,否则输出NIE。注意,员工的工资不能为负。
对于每个减工资的操作,何老板只是在心里想想,口头上说说,吓唬吓唬大家,解解闷气,他并不会真正执行。即不会对任何人的工资进行修改。
输入格式
第一行包含两个正整数n,m,分别表示员工的人数和操作次数。
接下来m行,每行一个操作,形式如题面所述。
输出格式
包含若干行,对于每个减工资操作,若可行,输出TAK,否则输出NIE。
样例输入 1
3 8
U 1 5
U 2 7
Z 2 6
U 3 1
Z 2 6
U 2 2
Z 2 6
Z 2 1
样例输出 1
NIE
TAK
NIE
TAK
样例输入 2
13 17
U 1 12
Z 1 9
Z 1 5
Z 4 7
U 7 18
Z 1 1
Z 1 8
U 6 4
U 1 9
U 3 13
Z 5 2
U 7 8
U 4 20
U 7 14
Z 6 1
Z 3 2
Z 8 7
样例输出 2
TAK
TAK
NIE
TAK
TAK
NIE
NIE
TAK
NIE
数据范围
对于30%的数据:1<=n,m<=1000
对于100%的数据:1<=n,m<=200000 1<=x<=n,0<=y<=10^9,1<=y<=10^9
题解
这道题看着很迷。。。
显然,对于某一组询问(x,y),如果一个人的工资大于y,无论有多高,这个人对扣除总工资的贡献都只能为y(毕竟一个人只能减y次)。而对于工资小于或等于y 的人来说,只要分配合理,他们的工资是可以完全利用的。
因此,设工资超过y 的人有k个,不超过y 的人的工资总和为sum,老板能完成操作当且仅当sum+y*k>=x*y。
然后考虑如何实现。
可以用俩树状数组。点修改和区间查询都能实现。具体见代码。
但是,考试时zz了一下,把方法看成了求第k小,就打了棵splay。发现错误后,改十几行就能通过。However,它的代码量是树状数组的3倍,时间是树状数组的2倍多,NKOJ甚至不加头文件优化过不了。。应该没人用这种方法。。。
代码
1:树状数组
#include <cstdio>
#include <iostream>
#include <algorithm>
#define int long long
using namespace std;
int maxn,u[2][1000000]={0};
int lb(int x)
{return x&(-x);}
void MDF(int x,int v,int q)
{while(x<=maxn)u[q][x]+=v,x+=lb(x);}
int SUM(int x,int q)
{int t=0;while(x)t+=u[q][x],x-=lb(x);return t;}
int uni[1000000],a[200005];
struct dt{
bool g;
int x,y;
}que[200005];
main()
{
int i,n,m;
scanf("%lld%lld",&n,&m);
for(i=1;i<=m;i++){
char o=getchar();
while(o!='U'&&o!='Z')o=getchar();
scanf("%lld%lld",&que[i].x,&que[i].y);
que[i].g=(o=='U');
uni[i]=que[i].y;
}
uni[m+1]=0;
sort(uni+1,uni+m+2);
maxn=unique(uni+1,uni+m+2)-uni-1;
for(i=1;i<=n;i++)a[i]=1;
MDF(1,n,0);
for(i=1;i<=m;i++)
if(que[i].g){
int x=que[i].x,y=lower_bound(uni+1,uni+maxn+1,que[i].y)-uni;
MDF(a[x],-1,0);
MDF(a[x],-uni[a[x]],1);
a[x]=y;
MDF(a[x],1,0);
MDF(a[x],uni[a[x]],1);
}
else{
int x=que[i].x,y=lower_bound(uni+1,uni+maxn+1,que[i].y)-uni;
int k=SUM(maxn,0)-SUM(y-1,0),yox=SUM(y-1,1);
yox>=(x-k)*que[i].y?puts("TAK"):puts("NIE");
}
return 0;
}
2:平衡树
#include <cstdio>
#include <cstdlib>
#include <iostream>
#define ll long long
using namespace std;
const int Q=2000000;
int ls[Q],rs[Q],f[Q],si[Q],cnt[Q],root=0,tot;
ll v[Q],sum[Q];
void upd(int y)
{sum[y]=sum[ls[y]]+sum[rs[y]]+v[y]*cnt[y];}
void lx(int x)
{
int y=f[x],z=f[y];
if(z)if(ls[z]==y)ls[z]=x;
else rs[z]=x;
f[x]=z;
rs[y]=ls[x];
f[rs[y]]=y;
ls[x]=y;
f[y]=x;
si[x]=si[y];
si[y]=si[ls[y]]+si[rs[y]]+cnt[y];
upd(y);upd(x);
}
void rx(int x)
{
int y=f[x],z=f[y];
if(z)if(ls[z]==y)ls[z]=x;
else rs[z]=x;
f[x]=z;
ls[y]=rs[x];
f[ls[y]]=y;
rs[x]=y;
f[y]=x;
si[x]=si[y];
si[y]=si[ls[y]]+si[rs[y]]+cnt[y];
upd(y);upd(x);
}
void splay(int x)
{
while(f[x]){
int y=f[x],z=f[y];
if(z)
if(ls[z]==y)
if(ls[y]==x)rx(y),rx(x);
else lx(x),rx(x);
else if(rs[y]==x)lx(y),lx(x);
else rx(x),lx(x);
else if(ls[y]==x)rx(x);
else lx(x);
}
root=x;
}
int ins(ll y)
{
if(!root){
root=++tot;
si[tot]=cnt[tot]=1;
v[tot]=sum[tot]=y;
return tot;
}
int t=root;
while(t)
{
++si[t];
sum[t]+=y;
if(v[t]==y)
{cnt[t]++;return t;}
if(y<v[t])
if(ls[t])t=ls[t];
else {ls[t]=++tot;break;}
else if(rs[t])t=rs[t];
else {rs[t]=++tot;break;}
}
f[tot]=t;
v[tot]=sum[tot]=y;
si[tot]=cnt[tot]=1;
splay(tot);
return tot;
}
int fin(ll y)
{
int t=root;
while(t)
{
if(v[t]==y&&cnt[t])return t;
if(y<v[t])t=ls[t];
else t=rs[t];
}
return -1;
}
void del(int x)
{
splay(x);
if((--cnt[x])>0){--si[x];return;}
int lls=ls[x],rrs=rs[x];
f[lls]=f[rrs]=ls[x]=rs[x]=si[x]=0;
if(!lls){root=rrs;return;}
root=lls;
while(rs[lls])lls=rs[lls];
splay(lls);
rs[lls]=rrs;
f[rrs]=lls;
si[lls]+=si[rrs];
}
ll a[200005];
int main()
{
int i,n,m,x;
ll y;
scanf("%d%d",&n,&m);
srand(n+m);
for(i=1;i<=n;i++)
ins(0);
while(m--){
char o=getchar();
while(o!='U'&&o!='Z')o=getchar();
scanf("%d%lld",&x,&y);
if(o=='U'){
del(fin(a[x]));
int p=fin(a[x]);
a[x]=y;
ins(a[x]);
}
else{
int p=root;
int gee=ins(y);
splay(gee);
ll temp=(ll)sum[ls[gee]]+(si[rs[gee]]+cnt[gee]-1)*y;
if(temp>=(ll)x*(ll)y)puts("TAK");
else puts("NIE");
del(gee);
}
splay(fin(a[rand()%n+1]));
}
return 0;
}