【NKOJ3776】工資管理
問題描述
何老闆的公司有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;
}