1. 程式人生 > 其它 >CodeForces - 1354D Multiset(樹狀陣列倍增找第k大)

CodeForces - 1354D Multiset(樹狀陣列倍增找第k大)

題目連結

題目大意

  模擬一下multiset,一共有兩個操作:
  1.插入一個數
  2.刪除第k小的數

解題思路

  用權值樹狀陣列來維護,主要是新學了樹狀陣列倍增的方法,可以比二分少一個log來查詢第k小的數,需要maxn是2的冪。
  比如要查詢第k小的數,我們倒著列舉二進位制位,如果區間\([0, 2^x]\)的數的數量比k要小,那麼說明第k小的數肯定是比\(2^x\)要大的,那麼根據二進位制的性質,這個數肯定是介於\([2^x, 2^{x+1}]\)之間的,那麼我們就用一個變數res加上\(2^x\),然後k減去其中的數量。再繼續列舉更低位,判斷區間\([res, res+2^y]\)

的數是否比k大,來繼續更新res和k的值,這樣可以再複雜度為log(n)的情況下找出第k小數。

const int maxn = 1<<20;
const int maxm = 2e5+10;
int n, q, c[maxn+1];
void add(int x, int y) {
    while(x<maxn) {
        c[x] += y;
        x += x&-x;
    }
}
int get_k(int x) {
    int res = 0;
    for (int i = maxn/2; i>=1; i>>=1) 
        if (c[res+i]<x) {
            res += i;
            x -= c[res];
        }
    return res+1;
}
int main(void) { 
    IOS;
    cin >> n >> q;
    for (int i = 1; i<=n; ++i) {
        int num; cin >> num;
        add(num, 1);
    }
    while(q--) {
        int num; cin >> num;
        if (num>0) add(num, 1);
        else {
            num = -num;
            int x = get_k(num);
            add(x, -1);
        }
    }
    int ans = 0;
    for (int i = 1; i<maxn; ++i)
        if (c[i]) {
            ans = i;
            break;
        }
    cout << ans << endl;
	return 0;   
}