洛谷 P1486 [NOI2004]郁悶的出納員【Treap】題解+AC代碼


題目描述

OIER公司是一家大型專業化軟件公司,有着數以萬計的員工。作為一名出納員,我的任務之一便是統計每位員工的工資。這本來是一份不錯的工作,但是令人郁悶的是,我們的老板反復無常,經常調整員工的工資。如果他心情好,就可能把每位員工的工資加上一個相同的量。反之,如果心情不好,就可能把他們的工資扣除一個相同的量。我真不知道除了調工資他還做什么其它事情。

工資的頻繁調整很讓員工反感,尤其是集體扣除工資的時候,一旦某位員工發現自己的工資已經低於了合同規定的工資下界,他就會立刻氣憤地離開公司,並且再也不會回來了。每位員工的工資下界都是統一規定的。每當一個人離開公司,我就要從電腦中把他的工資檔案刪去,同樣,每當公司招聘了一位新員工,我就得為他新建一個工資檔案。

老板經常到我這邊來詢問工資情況,他並不問具體某位員工的工資情況,而是問現在工資第k多的員工拿多少工資。每當這時,我就不得不對數萬個員工進行一次漫長的排序,然后告訴他答案。

好了,現在你已經對我的工作了解不少了。正如你猜的那樣,我想請你編一個工資統計程序。怎么樣,不是很困難吧?

如果某個員工的初始工資低於最低工資標准,那么將不計入最后的答案內

輸入格式:

第一行有兩個非負整數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是一個正整數。

在初始時,可以認為公司里一個員工也沒有。

輸出格式:

輸出文件的行數為F命令的條數加一。

對於每條F命令,你的程序要輸出一行,僅包含一個整數,為當前工資第k多的員工所拿的工資數,如果k大於目前員工的數目,則輸出-1。

輸出文件的最后一行包含一個整數,為離開公司的員工的總數。

輸入樣例#1:

9 10
I 60
I 70
S 50
F 2
I 30
S 15
A 5
F 1
F 2

輸出樣例#1:

10
20
-1
2

說明

I命令的條數不超過100000
A命令和S命令的總條數不超過100
F命令的條數不超過100000
每次工資調整的調整量不超過1000
新員工的工資不超過100000


也是比較裸的一題
說一下幾個坑點吧

  • 初始工資低於標准直接離開的人不計入離開的人總數(為這個找了好久 = =)
  • 刪除時記得更新總人數要用該結點的cnt,不要直接- -或++

還有不要吐槽蒟蒻神奇的刪除方式
(先記錄待刪除權值再查找刪除什么的 = =)
嗯更新工資也是
蒟蒻直接暴力把所有結點都遞歸更新了一遍

反正思路特直觀就對了= =


#include<iostream>
#include<vector>
#include<algorithm>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;

int read()
{
    int f=1,x=0;
    char ss=getchar();
    while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
    while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
    return f*x;
}

void print(int x)
{
    if(x<0){putchar('-');x=-x;}
    if(x>9)print(x/10);
    putchar(x%10+'0');
}

int n,minn;
int num,ans;
queue<int> q;
struct node
{
    node* ch[2];
    int v,r,sum,cnt;
    node(int v) :v(v) {r=rand();sum=cnt=1;ch[0]=ch[1]=NULL;};
    int cmp(int x){if(x==v)return -1;return x<v?0:1;}
    void update()
    {
        sum=cnt;
        if(ch[0]!=NULL) sum+=ch[0]->sum;
        if(ch[1]!=NULL) sum+=ch[1]->sum;
    }
};
node* rt=NULL;

void rotate(node* &p,int d)
{
    node* k=p->ch[d^1];
    p->ch[d^1]=k->ch[d];
    k->ch[d]=p;
    p->update(); k->update();
    p=k;
}

void ins(node* &p,int x)
{
    if(p==NULL){p=new node(x);return;}
    if(x==p->v){p->sum++;p->cnt++;return;}
    int d=p->cmp(x);
    ins(p->ch[d],x);
    if(p->ch[d]->r < p->r)rotate(p,d^1);
    p->update();
}

void up(node* &p,int x)
{
    if(p==NULL) return ;
    p->v+=x; 
    if(p->v<minn) 
    {
        q.push(p->v);
        int tot=p->cnt;
        ans+=tot;num-=tot;
        //總人數的更新用p->cnt
    }
    if(p->ch[0]!=NULL)up(p->ch[0],x);
    if(p->ch[1]!=NULL)up(p->ch[1],x);
}

void del(node* &p,int x){
    if(p==NULL) return;
    if(x==p->v)
    {
        if(p->ch[0]==NULL){node *k=p; p=p->ch[1]; delete(k); }
        else if(p->ch[1]==NULL){node *k=p; p=p->ch[0]; delete(k); }
        else
        {
            int dd=p->ch[0]->r < p->ch[1]->r ?1 :0;
            rotate(p,dd); del(p->ch[dd],x);
        }
    }

    else if(x < p->v)del(p->ch[0],x);
    else del(p->ch[1],x);
    if(p!=NULL)p->update();
}

int kth(node* p,int x)
{
    //找第k大同之前發過的模板,就不注釋了
    int sum=0;
    if(p->ch[1]!=NULL) sum=p->ch[1]->sum;
    if(x<=sum) return kth(p->ch[1],x);
    else if(x<=sum+p->cnt) return p->v;
    else return kth(p->ch[0],x-sum-p->cnt);
}

int main()
{
    n=read();minn=read();
    for(int i=1;i<=n;i++)
    {
        char ss; scanf("%s",&ss);
        int x=read();
        if(ss=='I')
        {
            if(x<minn)continue;//這里不用計入離開總人數
            else ins(rt,x),num++; 
        }
        else if(ss=='A') up(rt,x);
        else if(ss=='S') up(rt,-x);
        else if(ss=='F') 
        {
            if(x>num) print(-1);
            else print(kth(rt,x));
            printf("\n");
        }
        while(!q.empty()) {del(rt,q.front());q.pop();}
        //把之前記錄的權值都刪除
    }
    print(ans);
    return 0;
}

注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com