Loading

【题解】Luogu P8269 [USACO22OPEN] Visits S

思路

容易发现题目给出了一张 \(n\) 个点 \(n\) 条边的有向图,联想到基环树。又因为每个点出度均为一所以是内向基环树。

考虑到题目中的“拜访”类似于拓扑排序,冲突仅存在于环上,所以总边权和去掉环上最小的一条边即为答案。但题目不保证联通,所以其实是基环森林,每棵树上都有一个环。全部去掉即可。

代码

#include<iostream>
#include<cstdio>
#define int long long
using namespace std;
const int N=1e5+10;
int n,ans;
int a[N],w[N];
int ind[N],outd[N];
int vis[N];
int mn(int u){
    int minw=w[u],t=a[u];
    while(t!=u){
        minw=min(minw,w[t]);
        t=a[t];
    }
    return minw;
}
void change(int u){
    int t=u;
    while(vis[t]==1){
        vis[t]=2;//与判环的标记区分,防止汇入上一枝重新入环
        t=a[t];
    }
    return ;
}
signed main(){
	scanf("%lld",&n);
	for(int i=1;i<=n;i++){
		scanf("%lld%lld",&a[i],&w[i]);
		ans+=w[i];
	} 
	for(int i=1;i<=n;i++){
		if(!vis[i]){
            int t=i;
            while(!vis[t]){
                vis[t]=1;
                t=a[t];
            }//遍历这棵基环树直到出现环
            if(vis[t]==1) ans-=mn(t);//再搜一遍环求出最小边权
            change(i);//将走过的路全部标记,在从外向基环树的其他环外点搜索时不会再进入环内
		} 
	}
	printf("%lld\n",ans);
	return 0;
} 

时间复杂度 \(O(n)\)

posted @ 2025-12-12 22:38  Seqfrel  阅读(2)  评论(0)    收藏  举报