并查集

推荐:http://blog.csdn.net/dellaserss/article/details/7724401/

int father[maxn], size[maxn];
int fintFatherD(int x) {
    if (x == father[x]) {
        return x;
    } else {
        return father[x] = findFatherD(father[x]);
    }
}//路径压缩 
int findFather(int x) {
    int p, q;
    for (p = x; p != father[p]; p = father[p]);
    for (; x != p; q = father[x], father[x] = p, x = q);
    return p;
}//启发式合并 
void init(int n) {
    for (int i = 1; i <= n; ++ i) {
        father[i] = i;
        size[i] = 1;
    }
}//初始化 一开始每个人的父亲都是自己 
void joinSets(int a, int b) {
    a = findFather(a);
    b = findFather(b);
    if (size[a] < size[b]) {
        swap(a, b);
    }
    father[b] = a;
    size[a] += size[b];
}//把元素少的合并到元素大的  只后再把小的爸爸变成大的那个爸爸 

 例题:维护一个集合,支持插入一个数,查询一个数字前驱

#include <iostream>
#include <algorithm>
using namespace std;
const int maxn = 1000003;
int n, m, father[maxn], op[maxn], ans[maxn];
int findRoot(int x) {
    int p, q;
    for (p = x; p != father[p]; p = father[p]);
    for (; x != p; q = father[x], father[x] = p, x = q);
    return p;
}
void init(int n) {
    for (int i = 1; i <= n; ++ i) {
        father[i] = i - 1;
        size[i] = 1;
    }
}
int main() {
    cin >> n >> m;
    init(n);
    for (int i = 0; i < m; ++ i) {
        int opt, v;
        cin >> opt >> v;
        if (opt == 1) { // 插入
            father[v] = v;
            op[i] = -v;
        } else { // 查询
            op[i] = v;
        }
    }
    for (int i = m - 1; i >= 0; -- i) {
        if (op[i] < 0) { // 插入,现在要删除它
            father[-op[i]] = -op[i] - 1;
        } else { // 查询前趋
            ans[i] = findRoot(op[i] - 1);
        }
    }
    for (int i = 0; i < m; ++ i) {
        if (ans[i]) {
            cout << ans[i] << endl;
        }
    }
}//反向做题 先假设全部插入 之后倒序删除 答案再倒序输出 就是正的了 
//op数组记录操作 前驱就是集合中比他小的最大的数

 

posted @ 2017-01-25 16:10  Mr.9Pounds15Pence  阅读(113)  评论(0编辑  收藏  举报