基环树
基环树又称环套树。
其有以下特点
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);
}

浙公网安备 33010602011771号