codeforce 240E

时间:2021-10-17 16:44:04

/*

最小树形图+保存路径

第一次想错了,各种wa,tle后网上看资料,找到一篇错误的题解。。。

最后用对着正解分析了一波,感觉对最小树形图又有了新的理解:最小树形图的精髓在于每张图更新的时间信息!

第一次感觉到如此神奇的算法,解释分散在注释里了

pass:交到cf上时加文件输入输出语句才能过

*/

/*
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,,sizeof usedEdge);
int total=m,res=;
int u,v,w;
while(){
for(int i=;i<n;i++)in[i]=INF;
for(int i=;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=;i<n;i++)
if (i!=root && in[i]==INF) return -; int tn=;
memset(id,-,sizeof id);
memset(vis,-,sizeof vis);
in[root]=;
for(int i=;i<n;i++){
res+=in[i];
v=i;
//这条边被加入到当前图集合E中,这个点所在的边使用次数+1 !!!!
if(i!=root) usedEdge[preEdge[i]]++;
while(v!=root && id[v]==- && vis[v]!=i){
vis[v]=i;
v=pre[v];
}
if(id[v]==- && v!=root){
for(u=pre[v];u!=v;u=pre[u])
id[u]=tn;
id[v]=tn++;
}
}
if(tn==) break;
for(int i=;i<n;i++)
if(id[i]==-) id[i]=tn++; //准备更新旧图
for(int i=;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-;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=;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=;
int ans=zhuliu(root,n,m);
if(ans==- || ans==)
printf("%d\n",ans);
else {
printf("%d\n",ans);
for(int i=;i<m;i++)
if(edge[i].ww== && usedEdge[i])
printf("%d ",i+);
printf("\n");
}
}
return ;
}