http://acm.hdu.edu.cn/showproblem.php?pid=2121
学习了一蛤朱刘算法
这种随便找起始点的一般都是设一个超级源点连到所有点然后最后跑完再确定,之前有个插头DP也是这样的套路
设超级源点连到每个点一条sume+1的边
如果最后答案>=2*(sume+1)说明无解
由于超级源点只有连出去的边,那么我们每次都记录一下超级源点的赋值给谁作最小边,那么最后一次一定是超级源点连向某一个缩过的点,把这条边初始连向的点ov找到就行了,边的原始点是记录在数组里不变的
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxl=2e4+10;
const int inf=1e9+10;
int n,m,acrt;ll sume,ans;
int pre[maxl],vis[maxl],id[maxl];
ll ine[maxl];
struct ed
{
int ou,ov;
int u,v;ll w;
}e[maxl];
inline ll zhuliu()
{
ll ans=0;int rt=1;
while(1)
{
for(int i=1;i<=n;i++) ine[i]=inf;
for(int i=1;i<=m;i++)
{
int u=e[i].u,v=e[i].v;
if(u!=v && e[i].w<ine[v])
{
if(u==rt)
acrt=i;
ine[v]=e[i].w,pre[v]=u;
}
}
for(int i=1;i<=n;i++)
if(i!=rt && ine[i]==inf)
return -1;
int cnt=0;
for(int i=1;i<=n;i++)
vis[i]=id[i]=0;
for(int i=1;i<=n;i++)
{
if(i==rt) continue;
ans+=ine[i];
int v=i;
while(vis[v]!=i && !id[v] && v!=rt)// find circle
{
vis[v]=i;
v=pre[v];
}
if(!id[v] && v!=rt)//circle to point
{
id[v]=++cnt;
for(int u=pre[v];u!=v;u=pre[u])
id[u]=cnt;
}
}
if(cnt==0) break;//no circle
for(int i=1;i<=n;i++)
if(!id[i])
id[i]=++cnt;
for(int i=1;i<=m;i++)
{
int u=e[i].u,v=e[i].v;
e[i].u=id[u],e[i].v=id[v];
if(id[u]!=id[v])//ine[v] add to ans, more edge = del original edge
e[i].w-=ine[v];
}
rt=id[rt];n=cnt;
}
return ans;
}
inline void prework()
{
for(int i=1;i<=m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);u+=2;v+=2;
e[i]=ed{u,v,u,v,1ll*w};sume+=w;
}
n++;
for(int i=2;i<=n;i++)
e[++m]=ed{1,i,1,i,sume+1};
}
inline void mainwork()
{
ans=zhuliu();
if(ans>=(sume+1)*2)
ans=-1;
acrt=e[acrt].ov-2;
}
inline void print()
{
if(ans<0)
puts("impossible");
else
printf("%lld %d\n",ans-sume-1,acrt);
puts("");
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
prework();
mainwork();
print();
}
return 0;
}