codeforce 240E
/*
最小树形图+保存路径
第一次想错了,各种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,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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】