POJ 2985 The k-th Largest Group 第k大數 Treap / 樹狀陣列 + 並查集
阿新 • • 發佈:2019-02-03
題目連結
題意
有
法一:Treap
參考資料
注意點
- 無需首先將所有組全部插入。可以在
treap 中只維護size≥2 的組。注意一下合併時的erase 和insert 即可。 - 相同的元素無需多次插入。可以在每個節點上記一個
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;
}
法二:樹狀陣列
參考資料
思路
用樹狀陣列維護大小為
這裡可以用二分,還有一種更為巧妙的方法,具體見上面的連結_(:з」∠)_
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;
}