持续化并查集HDU 5923 B - Prediction

Describe

输入n,m,给你一个树有n个节点m-1条边,输入m-1个整数,第i个整数为节点i+1的父节点;然后输入m条边,表示一个图,输入q表示有q次询问:每次询问输入k和k个整数,k个整数表示树上节点编号,树上节点编号对应图中边节点编号,集合S由输入的树上节点编号以及所有祖先节点构成,对于每一个询问,输出由集合S引入边集合后,途中有几个联通块

https://blog.csdn.net/snowy_smile/article/details/52757816

Solution

依据dfs初始化并查集f[i][n],从上到下层层更新,表示引入i节点后(前面已经初始化好i的所有祖先节点了)的并查集联通情况,然后依据输入的节点更新一个并查集,最后判断有几个老大就可以了

Code

#include <bits/stdc++.h>
using namespace std;
const int maxn = 505,maxm = 1e4 + 10;
int n,m;
vector<int> mp[maxm];//树有m个点
pair<int,int> G[maxm];
int f[maxm][maxn];//对于树中的每一个节点都要遍历得到对应图中节点的状态

inline int Find(int f[],int x){
    return f[x] == x ? x : f[x] = Find(f,f[x]);
    //等号写成了 ==
}

inline void join(int f[],int a,int b){
    a = Find(f,a);
    b = Find(f,b);
    f[b] = a;
}

void dfs(int x,int fa){
    //状态叠加传递
    memcpy(f[x],f[fa],sizeof(f[fa]));
    //引入当前节点后状态改变
    join(f[x],G[x].first,G[x].second);
    //依据树节点向下遍历
    for(auto to : mp[x]){
        dfs(to,x);
    }
}
int d[maxn];
void solve(int cas){
    printf("Case #%d:\n",cas);
    int q;
    scanf("%d",&q);
    while(q--){
        int k,x;
        memcpy(d,f[0],sizeof(f[0]));
        scanf("%d",&k);
        while(k--){
            scanf("%d",&x);
            for(int i = 1;i <= n;++i){
                join(d,i,Find(f[x],i));
            }
        }
        int ans = 0;
        for(int i = 1;i <= n;++i)
            if(Find(d,i) == i) ++ans;
        printf("%d\n",ans);
    }
}
int main()
{
    int t;
    scanf("%d",&t);
    for(int cas = 1;cas <= t;++cas){
        scanf("%d%d",&n,&m);
        for(int i = 1;i <= m;++i)mp[i].clear();
        //树 m个节点
        int tmp;
        for(int i = 2;i <= m;++i){
            scanf("%d",&tmp);
            mp[tmp].push_back(i);
        }
        //图中的m条边
        for(int i = 1;i <= m;++i){
            scanf("%d%d",&G[i].first,&G[i].second);
        }
        for(int i = 1;i <= n;++i)f[0][i] = i;
        dfs(1,0);
        solve(cas);
    }
    return 0;
}

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值