Codeforces698 B. Fix a Tree(并查集)

    科技2022-07-15  130

    题意:

    给定长度为n的序列a, a(i)=x表示i有一条指向x的边, 如果图中只存在一个点,满足a[i]=i,那个这个点是树根, 问最少修改a序列多少个值,可以使得序列a表示一棵树。

    数据范围:n<=2e5

    解法:

    先找到一个a[i]=i的作为根(rt=i),那么其他a[x]=x的就不能作为根了,要改成指向根(a[x]=rt)。 (但是可能不存在a[i]=i的点i,也就是找不到根,这时图中一定存在环, 遇到环的时候,将形成环的边修改为指向节点自己,并令这个点为根)。

    并查集维护集合,因为树不允许存在环,当遇到环的时候,将环边改为指向根即可。

    code:

    #include<bits/stdc++.h> using namespace std; #define int long long const int maxm=2e5+5; int pre[maxm]; int a[maxm]; int n; int ffind(int x){ return pre[x]==x?x:pre[x]=ffind(pre[x]); } signed main(){ cin>>n; for(int i=1;i<=n;i++)pre[i]=i; for(int i=1;i<=n;i++)cin>>a[i]; int rt=0; for(int i=1;i<=n&&!rt;i++){//找一个根 if(a[i]==i&&!rt)rt=i; } int ans=0; for(int i=1;i<=n;i++){ if(i==rt)continue; if(a[i]==i){//改为指向根 ans++; a[i]=rt; pre[ffind(i)]=ffind(rt); }else{//a[i]!=i int x=ffind(a[i]),y=ffind(i); if(x!=y)pre[x]=y; else{//环 ans++; if(!rt){//如果没有根,那么改为指向自己,作为根 rt=i; a[i]=i; }else{ a[i]=rt; } pre[ffind(i)]=ffind(rt); } } } cout<<ans<<endl; for(int i=1;i<=n;i++)cout<<a[i]<<' '; return 0; }
    Processed: 0.013, SQL: 8