最小树形图问题,但是没有给定根,需要自己选根
可以加一个虚根,连向其他所有点,权值为sum=其他所有边权值之和+1
从虚根形成最小树形图,得到的答案ans需减去sum
如果ans==-1||ans>=sum说明不能形成树形图
中途需要记录最后连向虚根的边,这个边原本连向的另一个点就是选好的根
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
typedef long long ll;
const int N=1000+10;
const int M=1e4+1e3+10;
const ll INF=1e18;
struct Edge
{
int u,v;
ll w;
}edge[M];
int pre[N],vis[N],id[N];
ll in[N];
int p;
ll zhuliu(int root,int n,int m)
{
ll sum=0;
int u,v;
while(true)
{
for(int i=0;i<n;i++)
in[i]=INF;
for(int i=0;i<m;i++)
{
Edge e=edge[i];
if(e.u!=e.v&&e.w<in[e.v])
{
pre[e.v]=e.u;
in[e.v]=e.w;
if(e.u==root) p=i;
}
}
for(int i=0;i<n;i++)
if(i!=root&&in[i]==INF)
return -1;
int tn=0;
memset(id,-1,sizeof(id));
memset(vis,-1,sizeof(vis));
in[root]=0;
for(int i=0;i<n;i++)
{
sum+=in[i];
v=i;
while(vis[v]!=i&&id[v]==-1&&v!=root)
{
vis[v]=i;
v=pre[v];
}
while(v!=root&&id[v]==-1)
{
for(int u=pre[v];u!=v;u=pre[u])
id[u]=tn;
id[v]=tn++;
}
}
if(tn==0) break;
for(int i=0;i<n;i++)
if(id[i]==-1)
id[i]=tn++;
for(int i=0;i<m;i++)
{
v=edge[i].v;
edge[i].u=id[edge[i].u];
edge[i].v=id[edge[i].v];
if(edge[i].u!=edge[i].v)
edge[i].w-=in[v];
}
n=tn;
root=id[root];
}
return sum;
}
void solve(int n,int m,ll sum)
{
ll ans=zhuliu(n-1,n,m);
if(ans==-1||ans-sum>=sum) printf("impossible\n");
else
{
ans-=sum;
printf("%lld %d\n",ans,p-m+n-1);
}
printf("\n");
}
int main()
{
int n,m;
while(~scanf("%d%d",&n,&m))
{
int a,b,c;
ll sum=0;
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&a,&b,&c);
edge[i].u=a;
edge[i].v=b;
edge[i].w=c;
sum+=c;
}
sum++;
for(int i=0;i<n;i++)
{
edge[m].u=n;
edge[m].v=i;
edge[m++].w=sum;
}
solve(n+1,m,sum);
}
return 0;
}