1. 程式人生 > >bzoj1503: [NOI2004]鬱悶的出納員(平衡樹)

bzoj1503: [NOI2004]鬱悶的出納員(平衡樹)

原題連結

題目描述: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是一個正整數。
在初始時,可以認為公司裡一個員工也沒有。
I命令的條數不超過100000
A命令和S命令的總條數不超過100
F命令的條數不超過100000
每次工資調整的調整量不超過1000
新員工的工資不超過100000

輸出格式:輸出行數為F命令的條數加一。
對於每條F命令,你的程式要輸出一行,僅包含一個整數,為當前工資第k多的員工所拿的工資數,
如果k大於目前員工的數目,則輸出-1。
輸出檔案的最後一行包含一個整數,為離開公司的員工的總數。

輸入樣例
9 10
I 60
I 70
S 50
F 2
I 30
S 15
A 5
F 1
F 2

輸出格式
10
20
-1
2

解析:題目比較簡單,注意刪除時要整棵樹一起刪。
   全體一起加可以記錄一個add標記,表示整體加了多少。
   在插入時要插入k - add,因為add是整體所加上去的。
   刪除時把小於min - add的點刪去即可。
   最後注意一下細節好像就沒什麼了。

程式碼如下:

#include<cstdio>
using namespace std;

const int maxn = 1e5 + 5;
int n, m, add, root, ans;
int a[maxn], c[maxn], pri[maxn], lson[maxn], rson[maxn], cnt, size[maxn];
char s[5];

int read(void) {
    char c; while (c = getchar(), (c < '0' || c > '9') && c != '-'); int x = 0, y = 1;
    if (c == '-') y = -1; else x = c - '0';
    while (c = getchar(), c >= '0' && c <= '9') x = x * 10 + c - '0'; return x * y; 
}

int rand() { 
    static int seed = 2333;
    return seed = (int)((((seed ^ 998244353) + 19260817ll) * 19890604ll) % 1000000007);
}

void up(int k) {
    size[k] = size[lson[k]] + size[rson[k]] + c[k];
}

void zig(int &k) {
    int v = rson[k]; rson[k] = lson[v]; lson[v] = k;
    size[v] = size[k]; up(k); k = v;
}

void zag(int &k) {
    int v = lson[k]; lson[k] = rson[v]; rson[v] = k;
    size[v] = size[k]; up(k); k = v;
}

void insert(int &k, int x) {
    if (k == 0) {
      k = ++ cnt;
      size[k] = c[k] = 1; a[k] = x; pri[k] = rand();
      return;
    }
    size[k] ++;
    if (a[k] == x) c[k] ++;
    else if (x < a[k]) {
      insert(lson[k], x);
      if (pri[k] > pri[lson[k]]) zag(k);
    }
    else {
      insert(rson[k], x);
      if (pri[k] > pri[rson[k]]) zig(k);
    }
}

int del(int &k, int x) {
    if (k == 0) return 0; 
    int res = 0;
    if (a[k] < x) {
      res += size[lson[k]] + c[k]; 
      k = rson[k]; 
      res += del(k, x);
    }
    else res += del(lson[k], x);
    if (k) up(k);
    return res;
}

int query_num(int k, int x) {
    if (x <= size[lson[k]]) return query_num(lson[k], x);
    x -= size[lson[k]];
    if (x <= c[k]) return a[k];
    x -= c[k];
    return query_num(rson[k], x);
}

int main() {
    n = read(); m = read();
      for (int i = 1; i <= n; ++ i) {
        scanf("%s", s + 1); int x = read();
          if (s[1] == 'I') {
              if (x < m) continue; //注意不用累計答案 
              insert(root, x - add); //插入的是x - add 
            }
          else if (s[1] == 'A') {
            add += x; //累計標記 
          }
          else if (s[1] == 'S') {
            add -= x; //累計標記 
            ans += del(root, m - add);
          }
          else {
            if (x > size[root]) puts("-1");
            else printf("%d\n", query_num(root, size[root] - x + 1) + add);
          }
      }
    printf("%d", ans);
    return 0;
}