基环树

基环树又称环套树。
其有以下特点
1.n个点n条边
2.若整个图联通(没错,基环树其实是张图),那么有且仅有一个环,可以看作是一个树加了一条边

例题B. 复读机

参考代码

#include<bits/stdc++.h>
using namespace std;
const int MN=1e6+10;
void solve();
int main(){
    int tests=1;
    scanf("%d",&tests);
    while(tests--){
        solve();
    }
    return 0;
}
int n;
int a[MN],b[MN];
bool ic[MN];
int buk[MN];
int deg[MN];
int ccnt[MN];
void solve(){
    scanf("%d",&n);
    {
        map<int,int>mp;
        for(int i=1;i<=n;++i){
            scanf("%d",a+i);
            if(mp.find(a[i])==mp.end())mp[a[i]]=i;
        }//离散化
        for(int i=1;i<=n;++i)a[i]=mp[a[i]];
    }
    for(int i=1;i<=n;++i)buk[i]=0;
    for(int i=1;i<=n;++i)++buk[a[i]];//记录每个值出现的次数
    for(int i=1;i<=n;++i)ic[i]=1,deg[i]=0;
    for(int i=1;i<=n;++i)scanf("%d",b+i),++deg[b[i]];//入度
    {
        static int que[MN];
        int head=1,tail=0;
        for(int i=1;i<=n;++i)if(!deg[i])que[++tail]=i;
        while(head<=tail){
            int u=que[head++];
            ic[u]=0;//可进行拓扑排序的点
            if(!--deg[b[u]])que[++tail]=b[u];
        }
    }
    // for(int i=1;i<=n;++i)printf("%d%c",a[i]," \n"[i==n]);
    // for(int i=1;i<=n;++i)printf("%d%c",(int)ic[i]," \n"[i==n]);
 
    for(int i=1;i<=n;++i)ccnt[i]=0;
    int cc=0;
    for(int i=1;i<=n;++i)if(ic[i]){//找到环上的点
        ++cc;
        ic[i]=0;
        if(ccnt[a[i]]==cc-1)ccnt[a[i]]=cc;//这里相当于是个取并集的操作
        for(int x=b[i];x!=i;x=b[x]){
            ic[x]=0;
            if(ccnt[a[x]]==cc-1)ccnt[a[x]]=cc;
        }
    }
    int ans=n;
    for(int i=1;i<=n;++i)
    if(ccnt[i]==cc)ans=min(ans,n-buk[i]);
    printf("%d\n",ans==n?-1:ans);
}
posted @ 2022-08-16 18:53  何太狼  阅读(30)  评论(0)    收藏  举报