2021湖南多校对抗赛第三场 F - Fix a Tree(并查集)
题目链接
 题意:
给出一个图,图不一定连通,可能有环,给出每个点的父亲节点,问要最少要改变多少个点的父亲节点才能使得图为一棵树。
题解:
因为只给出每个点的父亲节点,则表明最多只有 n n n 条边,假如存在 a [ i ] = i a[i]=i a[i]=i ,说明点 i i i 所在的连通图必定是一棵树,没有环,为什么?假设 i i i 所在的连通图大小为 s s s ,因为 a [ i ] = i a[i]=i a[i]=i ,那么说明有效的边就是 s − 1 s-1 s−1 条,那不就是一棵树吗?
我们把 a [ i ] = i a[i]=i a[i]=i 的点当做树的根节点,用并查集去维护每个连通块,如果遍历到某个点存在环,那么直接把这个点的父亲节点变为树的根节点,上面说过,根节点的连通块一定无环,所以当前连通块一定不属于根节点,那么这么做就一定可以。
假如不存在 a [ i ] = i a[i]=i a[i]=i ,那么随便把某个环的点当做根节点。假如存在多个 a [ i ] = i a[i]=i a[i]=i ,那只取其中一个当做根节点,其他都看做是自环,依旧满足第一种做法。
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<stack>
#include<set>
#include<ctime>
#define iss ios::sync_with_stdio(false)
using namespace std;
typedef unsigned long long ull;
typedef long long ll;
const int mod=1e9+7;
const int MAXN=2e5+5;
const int inf=0x3f3f3f3f;
int fa[MAXN];
int a[MAXN];
int find(int x)
{
    if(fa[x]==x) return x;
    else return fa[x]=find(fa[x]);
}
int main()
{
//==========================================
#ifndef ONLINE_JUDGE
    freopen("1.in", "r", stdin);
    freopen("1.out", "w", stdout);
#endif
//==========================================
    int n;
    cin>>n;
    int root=0;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        if(a[i]==i) root=i;
        fa[i]=i;
    }
    int cnt=0;
    for(int i=1;i<=n;i++)
    {
        if(root==i) continue;
        int u=find(i),v=find(a[i]);
        if(u==v)
        {
            if(!root) root=i;
            a[i]=root;
            fa[u]=root;
            cnt++;
        }
        else
        {
            fa[u]=v;
        }
    }
    cout<<cnt<<endl;
    for(int i=1;i<=n;i++)
    {
        cout<<a[i]<<" ";
    }
}

 
                
             
         浙公网安备 33010602011771号
浙公网安备 33010602011771号