1. 程式人生 > >【NKOJ3776】工資管理

【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;
}