HDU 5923 Prediction (可持久化并查集带注释) ------待补

原题地址:http://acm.hdu.edu.cn/showproblem.php?pid=5923
题意:给你一个图 G V , E ), n 个点 m 条边。然后有一棵点数为 m 的树 T ,根为 1 , 树中每个点对应为图 G 的每条边

接下来有 q 次查询,每次查询有一个点数为 k 的集合 S , 如果一个树中一个点在集合 S 中,那么它的祖先也都要加入集合 S ,问由集合 S 组成的图 G 有多少个连通块

思路:可持久化并查集的应用,还不是特别懂

#include  <bits/stdc++.h>
using namespace std;
#define maxn 10005
/*
树中的节点是图的边的信息
树中每一个节点都保存着一个并查集,表示的是当前树中的节点以及其祖先节点保存的信息
并查集中保存的图中节点之间关系,是否是联通的
fa[i][j]=k,表示的是在树的第i个节点保存的并查集中,图的节点j与节点k相连(节点j的父节点是k)
*/
int fa[maxn][505], cnt[maxn];//cnt[u]表示连了树上的u节点表示的信息之后,图中还有几个联通分量
int n, m, q;
vector <int> g[maxn];
int e[maxn][2];

int Find (int i, int x) {//合并
    return fa[i][x] == x ? x : fa[i][x] = Find (i, fa[i][x]);
}


void dfs (int u, int father) {//初始化
    if (father != u) {//当前u不是根节点
        for (int i = 1; i <= n; i++) fa[u][i] = fa[father][i];//先复制
        cnt[u] = cnt[father];
    }
    int p1 = Find (u, e[u][0]), p2 = Find (u, e[u][1]);//修改树上的信息
    if (p1 != p2) {
        fa[u][p1] = p2;
        cnt[u]--;//联通块的数量
    }
    /*
    1 2
    2 3
    3 1
    这种情况
    */
    int sz = g[u].size ();
    for (int i = 0; i < sz; i++) {
        int v = g[u][i];
        dfs (v, u);
    }
}

int main () {
    int t, kase = 0;
    scanf ("%d", &t);
    while (t--) {
        scanf ("%d%d", &n, &m);
        for (int i = 1; i <= m; i++) g[i].clear ();
        for (int i = 2; i <= m; i++) {
            int father;
            scanf ("%d", &father);
            g[father].push_back (i);//树根连向树叶
        }
        for (int i = 1; i <= m; i++) {
            scanf ("%d%d", &e[i][0], &e[i][1]);//树的节点信息,表示图中哪两个节点联通
        }
        cnt[1] = n;
        for (int i = 1; i <= n; i++) fa[1][i] = i;//初始化并查集
        dfs (1, 1);
        scanf ("%d", &q);
        printf ("Case #%d:\n", ++kase);
        while (q--) {
            int num, u;
            scanf ("%d", &num);
            scanf ("%d", &u);
            for (int i = 1; i <= n; i++) fa[m + 1][i] = fa[u][i];//m+1这个是新的并查集
            int tmp = cnt[u];
            for (int i = 2; i <= num; i++) {
                scanf ("%d", &u);
                for (int j = 1; j <= n; j++) {//对比两个并查集的每一个位置
                    int x = Find (m + 1, j), y = Find (u, j);//Find第2维是节点,找到对应在并查集中根节点的编号
                    int p1 = Find (m + 1, x), p2 = Find (m + 1, y);//查询在的新的并查集中两者的根节点是否一样
                    if (p1 != p2) {//如果不一样就合并
                        tmp--;//联通分量数,如果连了一条边,那就说明来南通分量就会少一个
                        fa[m + 1][p1] = p2;//更新
                    }
                }
            }
            printf ("%d\n", tmp);
        }
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/yiqzq/article/details/81476159
今日推荐