Prediction 并查集 hdu-5923

题目链接:Problem - 5923

题面:

 

 

题意:

题意挺难理解的,有一张有n个点m条边的图,给定每个边的节点的父亲节点,现在这m条边组成了一棵根节点为1的魔术树(树的结点为边的id),然后给定这些边的节点,后面q次查询,查询这棵树的节点和所有父亲节点对应的边连接起来,一共有几个组件数量

思路:因为最后查询的集合内的内一个节点和所有父亲节点的联通图的组件数量,但是查询数量很大,我们不能反向查询父亲节点的方法去合并,但是我们可以通过预处理的方法,让儿子节点的并查集继承父亲节点, 然后通过把集合内点合并的方法就可以合并所需要的所有节点,然后遍历一遍,找几个组件数量即可

#include <bits/stdc++.h>
using namespace std;
#define endl "\n"
#define N 505
#define M 10005
int cnt = 0;
int to[M];
int h[M];
int nex[M];
int u[M], v[M];
int s[M][N];
int arr[300005];
void add(int x, int y){
    to[cnt] = y;
    nex[cnt] = h[x];
    h[x] = cnt++;
}
int findx(int x, int y){
    if(s[x][y] == y){
        return y;
    }
    return s[x][y] = findx(x,s[x][y]);
}
void merge(int m, int x, int y){
    int fx = findx(m, x);
    int fy = findx(m, y);
    if(fx != fy){
        s[m][fy] = fx;
    }
}
void dfs(int x, int f){
    memcpy(s[x], s[f], sizeof(s[x]));//儿子节点继承父亲节点的状态
    merge(x, u[x], v[x]);
    for(int i = h[x]; i != -1; i = nex[i]){
        int too = to[i];
        dfs(too, x);
    }
}
int main(){
    ios::sync_with_stdio(false);
    int t;
    cin >> t;
    int ans = 1;
    while(t--){
        cnt = 0;
        int n, m;
        cin >> n >> m;
        for(int i = 1; i <= m; i++){
            h[i] = -1;
        }
        for(int i = 2; i <= m; i++){
            int a;
            cin >> a;
            add(a, i);
        }
        for(int i = 1; i <= m; i++){
            cin >> u[i] >> v[i];
        }
        for(int i = 1; i <= n; i++) {
            s[1][i] = i;
        }
        dfs(1, 1);
        int q;
        cin >> q;
        cout << "Case #" << ans++ << ":" << endl;
        while(q--){
            int k;
            cin >> k;
            for(int i = 0; i < k; i++){
                cin >> arr[i];
            }
            memcpy(s[m + 1], s[arr[0]], sizeof(s[m + 1]));
            //把集合内剩下的点进行合并
            for(int i = 1; i < k; i++){
                for(int j = 1; j <= n; j++){
                    merge(m + 1, findx(arr[i], j), j);
                }
            }
            int num = 0;
            //查询有几个联通路
            for(int i = 1; i <= n; i++){
                if(findx(m + 1, i) == i){
                    num++;
                }
            }
            cout << num << endl;
        }
    }
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值