正题
评测记录:https://www.luogu.org/recordnew/lists?uid=52918&pid=P3651
题目大意
有n个点,有n条有向边,改变每一个点的出边需要价值不同,求最小价值使所以边的头尾都可以相互到达。
解题思路
首先我们可以找出所以的环,然后将环外的点加入他连接的环,最后枚举每个环的断开点,找到最优的一个地方断开。
code
#include<cstdio>
#include<algorithm>
#define N 100010
#define ll long long
using namespace std;
int n,l[N],c[N],mark[N];
ll ans,max1[N],max2[N];
int main()
{scanf("%d",&n);for(int i=1;i<=n;i++){scanf("%d%d",&l[i],&c[i]);}for(int i=1;i<=n;i++)//找环{if(mark[i]!=0) continue;int j=i;while(mark[j]==0){mark[j]=i;j=l[j];}if(mark[j]==i){int num=0;while(mark[j]!=-1){mark[j]=-1;j=l[j];num++;}//标记环if(num==n){printf("0");return 0;}//特判}}for(int i=1;i<=n;i++){ans+=c[i];max1[l[i]]=max(max1[l[i]],(ll)c[i]);if(mark[i]!=-1)max2[l[i]]=max(max2[l[i]],(ll)c[i]);}for(int i=1;i<=n;i++) ans-=max1[i];//计算环外价值for(int i=1;i<=n;i++){if (mark[i]!=-1) continue;int j=i;ll m=10000000000ll;while(mark[j]==-1){m=min(m,max1[j]-max2[j]);//最小的地方断开mark[j]=-2;j=l[j];}ans+=m;}//计算断开价值printf("%lld",ans);
}