/*
最小树形图+保存路径
第一次想错了,各种wa,tle后网上看资料,找到一篇错误的题解。。。
最后用对着正解分析了一波,感觉对最小树形图又有了新的理解:最小树形图的精髓在于每张图更新的时间信息!
第一次感觉到如此神奇的算法,解释分散在注释里了
*/
/* cf240e 最小树形图:输出路径板子 */ #include<iostream> #include<cstdio> #include<cstring> #define MAXN 100005 #define MAXM 2000005 #define ll long long #define INF 0x7fffffff using namespace std; int pre[MAXN],vis[MAXN],in[MAXN],id[MAXN]; int usedEdge[MAXN],preEdge[MAXN]; struct Edge{ int u,v,w,ww,id; Edge(){} Edge(int uu,int vv,int _w,int _ww,int ii):u(uu),v(vv),w(_w),ww(_ww),id(ii){} }edge[MAXM]; struct Used{ int pre,id; }cancle[MAXM];//保留每次更新图时被取消的边id int zhuliu(int root,int n,int m){ memset(usedEdge,0,sizeof usedEdge); int total=m,res=0; int u,v,w; while(1){ for(int i=0;i<n;i++)in[i]=INF; for(int i=0;i<m;i++){ u=edge[i].u,v=edge[i].v,w=edge[i].w; if(u!=v && w<in[v]){ in[v]=w; pre[v]=u; //记录这个顶点所在边的编号 !!! preEdge[v]=edge[i].id; } } 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++){ res+=in[i]; v=i; //这条边被加入到当前图集合E中,这个点所在的边使用次数+1 !!!! if(i!=root) usedEdge[preEdge[i]]++; while(v!=root && id[v]==-1 && vis[v]!=i){ vis[v]=i; v=pre[v]; } if(id[v]==-1 && v!=root){ for(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++){ u=edge[i].u,v=edge[i].v; edge[i].u=id[u],edge[i].v=id[v]; if(id[u]!=id[v]){ edge[i].w-=in[v]; //将该边在旧图中的编号取消 cancle[total].id=edge[i].id; cancle[total].pre=preEdge[v]; ///将这条边id重新编号 edge[i].id=total++; } } n=tn; root=id[root]; } //统计新建立(被重新编号)的边 !!!因为每重新编号一条边,就说明有一条边被取消一次,一因此需要将那条变得增加量也取消 //为什么要倒着往回遍历?因为必须沿着最终形成图的状态回溯逐步回溯到原图,找到边原始的id //肯定是新建立的边导致了之前的边被取消掉, for(int i=total-1;i>=m;i--) if(usedEdge[i]){//如果这条边被使用过(如果一条边仅仅是被重新编号而未被使用过,即只参与了边权值的改变但没有真正加入到某一次循环图中去,那么和其相关的边不用被取消) usedEdge[cancle[i].id]++;// usedEdge[cancle[i].pre]--;//把之前加的减掉 } return res; } int main(){ int n,m; while(~scanf("%d%d",&n,&m)){ for(int i=0;i<m;i++){ int u,v,w; scanf("%d%d%d",&u,&v,&w); u--;v--; edge[i]=Edge(u,v,w,w,i); } int root=0; int ans=zhuliu(root,n,m); if(ans==-1 || ans==0) printf("%d\n",ans); else { printf("%d\n",ans); for(int i=0;i<m;i++) if(edge[i].ww==1 && usedEdge[i]) printf("%d ",i+1); printf("\n"); } } return 0; }