洛谷P4716 【模板】最小树形图
题意:
n个点m条边得有向图,以s为根的最小树形图,及除s外其他点的入度为1
#include<bits/stdc++.h>
#define N 103
#define M 10002
#define INF 210000001
#define LL long long
using namespace std;
int read()
{
int x=0,f=1;char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}
return x*f;
}
struct EDGE{
int a,b,v;
}w[M];
int minn[N],id[N],vis[N],fa[N];
int cnt=0;
//cnt当前图环的数量
//id[u]代表u节点在第id[u]个环中
//vis[u]打标记避免一直走环
//minn[u]为当前连到u点的最短边的边权 fa[v]当前连到v点的最短边的u
LL ans=0;
int zhuliu(int n,int m,int r)
{
while(1)
{
for(int i=1;i<=n;++i) minn[i]=INF,id[i]=vis[i]=0;
for(int i=1;i<=m;++i)
{
if(w[i].a!=w[i].b&&w[i].v<minn[w[i].b])//不是自环 并且边权比选定的还小
fa[w[i].b]=w[i].a,minn[w[i].b]=w[i].v;
}
int u;
minn[r]=0;
for(int i=1;i<=n;++i)
{
if(minn[i]==INF)return 0;//存在一个不可以连接的点,什么不能找到最小树形图
ans+=minn[i];//这里就会更新到ans中
for(u=i;u!=r&&!id[u]&&vis[u]!=i;u=fa[u])vis[u]=i;//打上标记,这里走过的是一条链,vis打上i而不是1,因为可能多条边指向一个点
if(u!=r&&!id[u])//没有走到根,找到一个新环
{
id[u]=++cnt;
for(int v=fa[u];v!=u;v=fa[v])id[v]=cnt;
}
}
if(!cnt)return 1;//没有环了,说明现在就是最小树形图,边权和在上面就已经加入ans了
for(int i=1;i<=n;++i)
if(!id[i])id[i]=++cnt;//i节点不存在当前树中 就给他自己成一个环
for(int i=1;i<=m;++i)
{
int last=minn[w[i].b];//last等于当前连进v点的边的最小权值
if((w[i].a=id[w[i].a])!=(w[i].b=id[w[i].b]))w[i].v-=last;
//缩环的时候记得加入答案,环上的出边直接接上,入边要注意,选一条入边相当于删掉一条环边
//当前边的两个端点不在同一个环内
}
n=cnt;cnt=0;r=id[r];//缩完点后 当前点数就为环数 根节点就是根节点所在的环
}
}
int main()
{
int n=read(),m=read(),r=read();
for(int i=1;i<=m;++i)
w[i].a=read(),w[i].b=read(),w[i].v=read();
if(zhuliu(n,m,r))printf("%lld\n",ans);
else printf("-1\n");
}