标签:n+1 lse std ret code 出现 amp c++ 最小树形图
题意即求不定根最小树形图。
我们建一个虚拟节点\(n+1\)向\([1,n]\)连边权为所有边权之和的边,这样能保证这种边只会出现一次。之后跑最小树形图,并记录\(root\)连向的点是谁,那就是根。
code:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1010;
const int maxm=10010;
const ll inf=1e18;
int n,m,pos;
int vis[maxn],pre[maxn],col[maxn];
ll mindis[maxn];
struct Edge{int u,v;ll w;}E[maxm];
inline ll solve(int root,int n,int m)
{
ll res=0;
while(2333)
{
int cnt=0;
for(int i=1;i<=n;i++)pre[i]=vis[i]=col[i]=0,mindis[i]=inf;
for(int i=1;i<=m;i++)
if(E[i].u!=E[i].v&&E[i].w<mindis[E[i].v])
{
pre[E[i].v]=E[i].u,mindis[E[i].v]=E[i].w;
if(E[i].u==root)pos=i;
}
mindis[root]=0;
for(int i=1;i<=n;i++)
{
if(mindis[i]==inf)return -1;
res+=mindis[i];
int x=i;
while(x!=root&&vis[x]!=i&&!col[x])vis[x]=i,x=pre[x];
if(x!=root&&!col[x])
{
col[x]=++cnt;
int y=pre[x];
while(y!=x)col[y]=cnt,y=pre[y];
}
}
if(!cnt)return res;
for(int i=1;i<=n;i++)if(!col[i])col[i]=++cnt;
for(int i=1;i<=m;i++)
{
int delta=mindis[E[i].v];
E[i].u=col[E[i].u],E[i].v=col[E[i].v];
if(E[i].u!=E[i].v)E[i].w-=delta;
}
n=cnt;root=col[root];
}
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
ll sum=0;
for(int i=1;i<=m;i++)scanf("%d%d%lld",&E[i].u,&E[i].v,&E[i].w);
for(int i=1;i<=m;i++)E[i].u++,E[i].v++,sum+=E[i].w;
sum++;
for(int i=m+1;i<=n+m;i++)E[i]=(Edge){n+1,i-m,sum};
ll res=solve(n+1,n+1,n+m);
if(res==-1||res-sum>=sum)puts("impossible");
else printf("%lld %d\n",res-sum,pos-m-1);
puts("");
}
return 0;
}
标签:n+1 lse std ret code 出现 amp c++ 最小树形图
原文地址:https://www.cnblogs.com/nofind/p/12092801.html