1. 程式人生 > >黑匣子_NOI導刊2010提高 ---對頂堆/權值樹狀陣列

黑匣子_NOI導刊2010提高 ---對頂堆/權值樹狀陣列

創送門洛谷 P1801


題目大意

給定兩個操作

  1. A D D ( x ) ADD(x)
    :把 x x 元素放進 B l a c k
    B o x BlackBox
  2. G E T
    GET
    : i i 1 1 ,然後輸出 B l a c k h o x Blackhox 中第 i i 小的數( i i 的初值為 0 0 )

分析

題意很明瞭,就是動態地求區間第 k k 大值。
方法有很多,這裡提供兩種。

  1. 權值線段樹/樹狀陣列
    離散化是不用說的吧
    如果做過逆序對的話應該會比較熟悉。用每個節點表示該點對應的數值是否存在。要找第 k k 大值的話只需要找到字首和為 k k 的點即可。
    樹狀陣列的求法可以說是查詢字首和的逆操作,明確好每個節點管理的範圍,模擬模擬即可,保證不會重複計算的。
    x x 所管理的區間 [ x l o w b i t ( x ) + 1 , x ] [x - lowbit(x) + 1, x]
  2. 對頂堆
    利用一個小根堆與一個大根堆來維護序列。
    將前 k k 小的數放到大根堆去,其餘的全部丟到小根堆。實時維護即可。

程式碼

  • 權值樹狀陣列
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>- **權值樹狀陣列**

#define IL inline

using namespace std;

IL int read()
{
    char c = getchar();
    int sum = 0 ,k = 1;
    for(;'0' > c || c > '9'; c = getchar())
        if(c == '-') k = -1;
    for(;'0' <= c && c <= '9'; c = getchar()) sum = sum * 10 + c - '0';
    return sum * k;
}

int qes[2][200005];
int cnt, num[200005];
int bit[200005];
int power[20];
int limit;

IL int lowbit(int x) { return x& (-x); }
IL void add(int x)
{
    for(; x <= cnt; x += lowbit(x))
        ++bit[x];
}
IL int find_k(int k)
{
    int ans = 0, tot = 0;
    for(int i = limit; i >= 0; --i)
    {
        ans += power[i];
        if(ans >= cnt || tot + bit[ans] >= k)
            ans -= power[i];	
        else
            tot += bit[ans];
    }
    return ans + 1;
}

int main()
{
    int n = read(), m = read();
    
    power[0] = 1;
    for(int i = 1; i; ++i)
    {
    	power[i] = power[i - 1] << 1;
    	if(power[i] > n)
    	{
    		limit = i - 1;
    		break;
    	}
    }
    for(int i = 1; i <= n; ++i)
    {
    	num[++cnt] = qes[0][i] = read();
    }
    for(int i = 1; i <= m; ++i)
    	qes[1][i] = read();
    sort(num + 1, num + cnt + 1);
    cnt = unique(num + 1, num + cnt + 1) - (num + 1);
    for(int i = 1, j = 1, x, t = 0; i <= n; ++i)
    {
    	x = lower_bound(num + 1, num + cnt + 1, qes[0][i]) - num;
    	add(x);
    	for(; j <= m && qes[1][j] == i; ++j)
    	{
    		printf("%d\n", num[find_k(++t)]);
    	}
    }
    return 0;
}
  • 對頂堆
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <queue>

#define IL inline

using namespace std;

IL int read()
{
    char c = getchar();
    int sum = 0 ,k = 1;
    for(;'0' > c || c > '9'; c = getchar())
        if(c == '-') k = -1;
    for(;'0' <= c && c <= '9'; c = getchar()) sum = sum * 10 + c - '0';
    return sum * k;
}

int K = 0;
struct node
{
    priority_queue<int>Q1;
    priority_queue<int , vector<int>, greater<int> >Q2;
    int t1, t2;
    
    IL void change()
    {	
        for(; t1 > K;) { Q2.push(Q1.top()); Q1.pop(); ++t2; --t1; }
        for(; t1 < K;) { Q1.push(Q2.top()); Q2.pop(); ++t1; --t2; }
    }
    
    IL void add(int x)
    {	
        if(!t1 || Q1.top() >= x) { Q1.push(x); ++t1;}
        else { Q2.push(x); ++t2; }
        
        change();
    }
    
    IL int get()
    {
        return Q1.top();
    }
}a;

int qes[2][200005];

int main()
{
    int n = read(), m = read();
    
    for(int i = 1; i <= n; ++i) qes[0][i] = read();
    for(int i = 1; i <= m; ++i) qes[1][i] = read();
    
    for(int i = 1, j = 1; i <= n && j <= m; ++i)
    {
    	a.add(qes[0][i]);
    	for(; j <= m && qes[1][j] == i; ++j)
    	{
    		++K;
    		a.change();
    		printf("%d\n", a.get());
    	}
    }
    return 0;
}