1. 程式人生 > >POJ 2985 The k-th Largest Group 第k大數 Treap / 樹狀陣列 + 並查集

POJ 2985 The k-th Largest Group 第k大數 Treap / 樹狀陣列 + 並查集

題目連結

題意

n 只貓,m 次操作(n,m2e5):
0ij:將第 i 只貓所在組與第 j 只貓所在組合並;
1k:詢問第 k 大的組中有多少隻貓。

法一:Treap

參考資料

注意點

  1. 無需首先將所有組全部插入。可以在 treap 中只維護 size2 的組。注意一下合併時的 eraseinsert 即可。
  2. 相同的元素無需多次插入。可以在每個節點上記一個 cnt.

Code

#include <cstdio>
#include <climits>
#include <cstdlib>
#include <iostream>
#define maxn 200010 using namespace std; int fa[maxn], sz[maxn]; struct node { node* ch[2]; int val, key, sz, cnt; node() { sz = 0, cnt = 0, key = INT_MAX; } node(int x); void update() { sz = ch[0]->sz + ch[1]->sz + cnt; } }*null = new node; node::node(int x) { ch[0] = ch[1] = null; val = x, key = rand(), sz = 1
, cnt = 1; } struct treap { node* root; treap() { root = null; } void rotate(node*& t, bool d) { node* p = t->ch[d]; t->ch[d] = p->ch[!d]; p->ch[!d] = t; t->update(); p->update(); t = p; } void insert(node*& t, int
x) { if (t == null) { t = new node(x); return; } if (t->val == x) { ++t->cnt; t->update(); return; } bool dir = x > t->val; insert(t->ch[dir], x); if (t->ch[dir]->key < t->key) rotate(t, dir); else t->update(); } void erase(node*& t, int x) { if (t == null) return; if (t->val == x) { if (t->cnt > 1) { --t->cnt, t->update(); return; } bool d = t->ch[1]->key < t->ch[0]->key; if (t->ch[d] == null) { delete t; t = null; return; } if (t->ch[!d] == null) { node* p = t->ch[d]; delete t; t = p; return; } rotate(t, d); erase(t->ch[!d], x); t->update(); return; } bool d = x > t->val; erase(t->ch[d], x); t->update(); } int calckth(int k) { if (k > root->sz) return 1; k = root->sz-k+1; int dir; for (node* t = root; t != null; t = t->ch[dir]) { if (k <= t->ch[0]->sz) dir = 0; else if (k - t->ch[0]->sz - t->cnt <= 0) return t->val; else dir = 1, k -= (t->ch[0]->sz + t->cnt); } } void insert(int x) { insert(root, x); } void erase(int x) { erase(root, x); } int size() { return root->sz; } }* Treap; int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); } void unionn(int u, int v) { int fau = find(u), fav = find(v); if (fau == fav) return; if (sz[fau] > 1) Treap->erase(sz[fau]); if (sz[fav] > 1) Treap->erase(sz[fav]); if (sz[fau] > sz[fav]) swap(fau, fav); fa[fau] = fav, sz[fav] += sz[fau]; Treap->insert(sz[fav]); } int n, m; void work() { Treap = new treap; for (int i = 1; i <= n; ++i) fa[i] = i, sz[i] = 1; while (m--) { int x, u, v, k; scanf("%d", &x); if (x == 0) { scanf("%d%d", &u, &v); unionn(u, v); } else { scanf("%d", &k); printf("%d\n", Treap->calckth(k)); } } } int main() { while (scanf("%d%d", &n, &m) != EOF) work(); return 0; }

法二:樹狀陣列

參考資料

思路

用樹狀陣列維護大小為 size 的組的數量,然後找其中的第 k 大組。
這裡可以用二分,還有一種更為巧妙的方法,具體見上面的連結_(:з」∠)_

Code

#include <cstdio>
#include <climits>
#include <cstdlib>
#include <iostream>
#include <cstring>
#define maxn 200010
using namespace std;
int fa[maxn], sz[maxn], n, m, c[maxn], num;
int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }
int lowbit(int x) { return x & (-x); }
void add(int i, int x) { for (; i <= n; i += lowbit(i)) c[i] += x; }
int sum(int i) { int ret = 0; for (; i; i -= lowbit(i)) ret += c[i]; return ret; }
void unionn(int u, int v) {
    int fau = find(u), fav = find(v);
    if (fau == fav) return;
    add(sz[fau], -1), add(sz[fav], -1);
    if (sz[fau] > sz[fav]) swap(fau, fav);
    fa[fau] = fav; sz[fav] += sz[fau];
    add(sz[fav], 1);
    --num;
}
//int calc(int k) {
//    int l = 1, r = n;
//    while (r-l > 1) {
//        int mid = l + r >>1;
//        if (sum(mid) >= k) r = mid;
//        else l = mid + 1;
//    }
//    if (sum(l) >= k) return l;
//    else return r;
//}
int calc(int k) {
    int cnt = 0, ans = 0;
    for (int i = 20; i >= 0; --i) {
        ans += (1 << i);
        if (ans > n || cnt + c[ans] >= k) ans -= (1 << i);
        else cnt += c[ans];
    }
    return ans + 1;
}
void work() {
    memset(c, 0, sizeof(c));
    for (int i = 1; i <= n; ++i) fa[i] = i, sz[i] = 1;
    add(1, n);
    num = n;
    while (m--) {
        int x, u, v, k;
        scanf("%d", &x);
        if (x == 0) {
            scanf("%d%d", &u, &v);
            unionn(u, v);
        }
        else {
            scanf("%d", &k);
            printf("%d\n", calc(num-k+1));
        }
    }
}
int main() {
    while (scanf("%d%d", &n, &m) != EOF) work();
    return 0;
}