• 并查集


    POJ 1182 食物链

    添加一个维护当前节点与父节点关系的信息,每次压缩时更新关系,另外join的时候也要根据当前节点和父节点关系以及两个父节点关系更新;

    单case,不用EOF处理。

    # include <cstdio>
    const int maxn = 50005;
    int n, k;
    int p[maxn];
    int g[maxn];
    int find(int x) {
        if (x == p[x]) return x;
        int t = find(p[x]);
        g[x] = ((g[p[x]] + g[x]) % 3 + 3) % 3;
        p[x] = t;
        return t;
    }
    # define testin freopen("in.txt", "r", stdin)
    int main()
    {
        //while (scanf("%d%d", &n, &k) != EOF) {
            scanf("%d%d", &n, &k);
            for (int i = 1; i <= n; ++i) p[i] = i, g[i] = 0;
            int ans = 0;
            for (int i = 0, od, x, y; i < k; ++i) {
                scanf("%d%d%d", &od, &x, &y);
                if (x>n||y>n || (od==2 && x==y)) {
                    ++ans;
                    continue;
                } else {
                    int fx = find(x);
                    int fy = find(y);
                    if (fx != fy) {
                        p[fx] = fy;
                        g[fx] = ((g[y]-g[x]+od-1)%3+3)%3;
                    } else if (((g[x]-g[y])%3+3)%3 != od-1){
                        ++ans;
                    }
                }
            }
            printf("%d
    ", ans);
        //}
    
        return 0;
    }

    HDU 2473

    带删除节点的并查集

    对每个节点,映射射到一个虚拟节点,然后对虚拟节点维护并查集,删除节点时,将对应节点应射到一个新的节点即可,由于查询次数有限,浪费的空间可以接受。

    # include <cstdio>
    const int maxn = 100005;
    const int maxm = 1000005;
    int n, m;
    int fa[maxn + maxm];
    bool isRoot[maxn + maxm];
    int link[maxn];
    int id;
    int find(int x) {
        if (x != fa[x]) return fa[x]=find(fa[x]);
    }
    void join(int x, int y)
    {
        x = find(x);
        y = find(y);
        if (x != y) fa[x] = y;
    }
    void del(int x)
    {
        link[x] = id++;
    }
    # define testin freopen("in.txt", "r", stdin)
    int main()
    {
        int ica = 0;
        while (scanf("%d%d", &n, &m), n||m) {
            for (int i = 0; i < n+m; ++i) isRoot[i]=false, fa[i]=i;
            for (int i = 0; i < n; ++i) link[i] = i;
            char od[5];
            id = n;
            for (int i = 0, x, y; i < m; ++i) {
                scanf("%s%d", od, &x);
                if (od[0] == 'M') {
                    scanf("%d", &y);
                    join(link[x], link[y]);
                } else {
                    del(x);
                }
            }
            int ans = 0;
            for (int i = 0; i < n; ++i) {
                if (!isRoot[ find(link[i]) ]) {
                    ++ans;
                    isRoot[ find(link[i]) ] = true;
                }
            }
            printf("Case #%d: %d
    ", ++ica, ans);
        }
        return 0;
    }

    ZOJ 3261

    带删除边的并查集。

    WA:维护的并不是树,每个节点和多个节点直接有关系,不能直接删除并查集中的边。

    WA 了很多次,原因找到了:再离线保存query处理destroy的情况时,首先保存了query[i][1] = x, 然后条件交换了x,y,然后query[i][2]=y。(x>y时,导致query[i][2]==x)

    # include <map>
    # include <utility>
    # include <algorithm>
    # include <cstdio>
    # include <cstring>
    
    const int maxn = 10005;
    const int maxm = 20005;
    const int maxq = 50005;
    
    int n, m, q;
    int power[maxn];
    int u[maxm], v[maxm];
    bool des[maxm];
    int query[maxq][3];
    int p[maxn];
    
    std::map< std::pair<int, int>, int >mp;
    
    int find(int x) {
        return x==p[x] ? x:p[x]=find(p[x]);
    }
    void join(int x, int y) {
        x = find(x), y = find(y);
        if (x == y) return ;
        if (x > y) std::swap(x, y);
        if (power[x] < power[y]) p[x] = y;
        else p[y] = x;
    }
    # define testin freopen("in.txt", "r", stdin)
    int main()
    {
        bool first = true;
    
        while (scanf("%d", &n) != EOF) {
            mp.clear();
            for (int i = 0; i < n; ++i) scanf("%d", &power[i]);
            scanf("%d", &m);
            for (int i = 0; i < m; ++i) {
                scanf("%d%d", &u[i], &v[i]);
                if (u[i] > v[i]) std::swap(u[i], v[i]);
                mp[std::make_pair(u[i], v[i])] = i+1;
            }
            memset(des, false, sizeof(des[0])*m);
            scanf("%d", &q);
            char od[15];
            for (int i = 0, x, y; i < q; ++i) {
                scanf("%s%d", od, &x);
                if (od[0] == 'q') {
                    query[i][0] = 0;
                    query[i][1] = x;
                } else {
                    scanf("%d", &y);
                    if (x > y) std::swap(x, y);
                    int t = mp[ std::make_pair(x,y) ] - 1;
                    if (t >= 0) des[t] = true;
                    query[i][0] = 1;
                    query[i][1] = x;
                    query[i][2] = y;
                }
            }
            for (int i = 0; i < n; ++i) p[i] = i;
            for (int i = 0; i < m; ++i) {
                if (!des[i]) {
                    join(u[i], v[i]);
                }
            }
            for (int i = q-1; i >= 0; --i) {
                if (!query[i][0]) {
                    int t = find( query[i][1] );
                    if (power[t] <= power[ query[i][1] ]) t = -1;
                    query[i][2] = t;
                } else {
                    join( query[i][1], query[i][2] );
                }
            }
            if (first) first = false;
            else printf("
    ");
            for (int i = 0; i < q; ++i) {
                if (!query[i][0]) printf("%d
    ", query[i][2]);
            }
        }
    
        return 0;
    }

    cf 356A Knight Tournament

    感觉这是一道并查集的好题,记得当时想了很久没思路,赛后看别人的代码也没弄明白。

    其实这道题完全体现了并查集的用途:维护集合;不同点在于需要如何维护,即并的方式。

    当x打败y时,只要将ans[y]更新为x即可,所以并查集维护的并不是淘汰关系。

    将没有淘汰的选手作为根节点,两个未淘汰选手x,y(x<y)之间的淘汰了的选手加入到y的集合中,即当前节点x的根节点是右侧第一个未被淘汰的节点。

    # include <stdio.h>
    using namespace std;
    
    const int maxn = 300005;
    
    int n, m;
    int p[maxn];
    int ans[maxn];
    
    int find(int x) {
        return x==p[x] ? x:p[x]=find(p[x]);
    }
    void join(int l, int r, int x)
    {
        while ((l=find(l)) <= r) {
            if(l != x) ans[l] = x, p[l] = l+1;
            ++l;
        }
    }
    int main()
    {
        scanf("%d%d", &n, &m);
        for (int i = 1; i <= n+1; ++i) p[i] = ans[i] = i;
        for (int i = 1, x, l, r; i <= m; ++i) {
            scanf("%d%d%d", &l, &r, &x);
            join(l, r, x);
        }
        for (int i = 1; i <= n; ++i) {
            if (i > 1) putchar(' ');
            printf("%d", (ans[i]==i ? 0:ans[i]));
        }
        putchar('
    ');
    
        return 0;
    }
  • 相关阅读:
    Python 以指定列宽格式化字符串
    Windows环境下QWT安装及配置
    iOS用户体验之-modal上下文
    android-调用系统的ContentPrivder获取单张图片实现剪切做头像及源代码下载
    Codeforces Round #253 (Div. 1) A Borya and Hanabi
    剑指offer 面试题9
    Memcache应用场景介绍
    [Unity-22] Coroutine协程浅析
    ZOJ 2706 Thermal Death of the Universe (线段树)
    为什么不建议用Table布局
  • 原文地址:https://www.cnblogs.com/txd0u/p/4147534.html
Copyright © 2020-2023  润新知