POJ2985-The k-th Largest Group(Treap + 并查集)

The k-th Largest Group
Time Limit: 2000MS Memory Limit: 131072K
Total Submissions: 9639 Accepted: 3142
Description

Newman likes playing with cats. He possesses lots of cats in his home. Because the number of cats is really huge, Newman wants to group some of the cats. To do that, he first offers a number to each of the cat (1, 2, 3, …, n). Then he occasionally combines the group cat i is in and the group cat j is in, thus creating a new group. On top of that, Newman wants to know the size of the k-th biggest group at any time. So, being a friend of Newman, can you help him?

Input

1st line: Two numbers N and M (1 ≤ N, M ≤ 200,000), namely the number of cats and the number of operations.

2nd to (m + 1)-th line: In each line, there is number C specifying the kind of operation Newman wants to do. If C = 0, then there are two numbers i and j (1 ≤ i, j ≤ n) following indicating Newman wants to combine the group containing the two cats (in case these two cats are in the same group, just do nothing); If C = 1, then there is only one number k (1 ≤ k ≤ the current number of groups) following indicating Newman wants to know the size of the k-th largest group.

Output

For every operation “1” in the input, output one number per line, specifying the size of the kth largest group.

Sample Input

10 10
0 1 2
1 4
0 3 4
1 2
0 5 6
1 1
0 7 8
1 1
0 9 10
1 1
Sample Output

1
2
2
2
2
Hint

When there are three numbers 2 and 2 and 1, the 2nd largest number is 2 and the 3rd largest number is 1.
题目:这里写链接内容
题意:有n只猫咪,起初它们各自为一组,组成n个组,现在有一些操作,输入一个c,若c=1,则输入a,b,表示将a,b所在的一组合并再一起(若a,b已经在一个组,则不进行任何操作),若c=0,则输入k,表示查找第k大的组。
思路:正解好像是线段是+并查集,但红书的Treap模板推荐题目是这一道,所以拿来练练手。
具体思路是,使用并查集维护组,当合并时,先在树中将a,b所在组的数值删除,再将合并后的值加入。查询第K大时直接调用函数即可。
不过直接套用模板+并查集会re,因为红书的模板中,删除某个键值时,并不是真的删除,只是将该键值的计数置0,每次加入一个不同键值时,节点的规模会一直增长, 由于此题存在多次删除和插入操作,最终会造成下标越界。所以这里要动态的开辟节点。
代码:

#include<cstdio>
#include<algorithm>
#include <ctime>

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int maxn = 1e7 + 10;//代码和红书上基本相同,只是把全部数组改成了指针操作


int random() {//如果调用rand()函数,好像会慢一点,实测用random()是688ms,rand()是704ms,差距不大,调用rand()函数并且让随机种子为时间的话,可以有效避免被卡
    int inter = 823;
    return inter = int(inter * 48271LL % 2147483647);
}

struct Treap {

    struct node {
        int key, weight, cnt, size;
        node *childs[2];

        void init(int x) {
            key = x;
            weight = random();
            cnt = 1;
            size = 1;
            childs[0] = childs[1] = NULL;
        }
    };

    node *root;

    void update(node *&x) {
        if (x == NULL)
            return;
        int right = 0, left = 0;
        if (x->childs[0] != NULL)right = x->childs[0]->size;
        if (x->childs[1] != NULL)left = x->childs[1]->size;
        x->size = right + left + x->cnt;
    }

    void rotate(node *&x, int t) {
        node *y = x->childs[t];
        x->childs[t] = y->childs[1 - t];
        y->childs[1 - t] = x;
        update(x);
        update(y);
        x = y;
    }

    void _insert(node *&x, int k) {
        if (x != NULL) {
            if (x->key == k) {
                x->cnt++;
            } else {
                int t = x->key < k;
                _insert(x->childs[t], k);
                if (x->childs[t]->weight < x->weight) {
                    rotate(x, t);
                }
            }
        } else {
            x = (node *) malloc(sizeof(node));
            x->init(k);
        }
        update(x);
    }

    void _erase(node *&x, int k) {
        if (x->key == k) {
            if (x->cnt > 1) {
                x->cnt--;
            } else {//如果被删除节点存在子节点,先将其旋转至底层再删除
                if (x->childs[0] == NULL && x->childs[1] == NULL) {
                    x->cnt = 0;
                    return;
                }
                int t;
                if (x->childs[0] == NULL)t = 1;
                else if (x->childs[1] == NULL)t = 0;
                else t = x->childs[0] > x->childs[1];
                rotate(x, t);
                _erase(x, k);
            }
        } else {
            int t = x->key < k;
            _erase(x->childs[t], k);
            if (x->childs[t]->cnt == 0)free(x->childs[t]), x->childs[t] = NULL;//动态内存释放
        }
        update(x);
    }

    int _getkth(node *&x, int k) {
        int t = 0;
        if (x->childs[1] != NULL)t = x->childs[1]->size;
        if (k <= t)return _getkth(x->childs[1], k);
        k -= t + x->cnt;
        if (k <= 0)return x->key;
        return _getkth(x->childs[0], k);
    }

    void insert(int k) {
        _insert(root, k);
    }

    void erase(int k) {
        _erase(root, k);
    }

    int getkth(int k) {
        return _getkth(root, k);
    }
};

Treap tree;
int father[maxn][2], num1, num;

int find_father(int x) {
    return father[x][0] == x ? x : father[x][0] = find_father(father[x][0]);
}

void merge(int x, int y) {
    int a = find_father(x);
    int b = find_father(y);
    if (a == b)return;
    num--;
    if (father[a][1] == 1)num1--;
    else tree.erase(father[a][1]);
    if (father[b][1] == 1)num1--;
    else tree.erase(father[b][1]);
    father[b][0] = a;
    father[a][1] += father[b][1];
    tree.insert(father[a][1]);
}

int main() {
    //srand((unsigned) time(NULL));
    int n, m;
    tree.root = NULL;
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= n; ++i)father[i][0] = i, father[i][1] = 1;
    num = num1 = n;
    for (int i = 1; i <= m; i++) {
        int c, a, b;
        scanf("%d", &c);
        if (c == 0) {
            scanf("%d%d", &a, &b);
            merge(a, b);
        } else {
            scanf("%d", &a);
            if (num - a < num1)printf("1\n");
            else printf("%d\n", tree.getkth(a));
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/swust5120160705/article/details/80181694