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

題目鏈接

題意

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

法一:Treap

參考資料

董的博客 數據結構之Treap
clj的treap ——wbysr
POJ 2985 Treap平衡樹(求第k大的元素) ——瀟灑走一回LW

注意點

  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;
}

法二:樹狀數組

參考資料

樹狀數組:第K大值 ——oa414

思路

用樹狀數組維護大小爲 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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章