【题意】给定一个联通图,求这个图的最小生成树的不可替代边有哪些,并计算这些边的总权值。
【分析】先任意求出一颗生成树,然后标记树边和非树边。然后枚举一下非树边,对于每条边u,v,去找MST上从u到v的路径上是否存在某条边的权值等于这条边的权值。如果有,说明是可替代的。这里直接暴力u,v之间的路径或者求lca即可。
【AC 代码】
#include <bits/stdc++.h>
using namespace std;
const int maxn = 550;
const int maxm = 50050;
struct node1{
int u,v,w;
int vis;
bool operator<(const node1 &rhs)const{
return w<rhs.w;
}
}E1[maxm];
int head[maxn],fa[maxn],dep[maxn],tot,mst,n,m;
struct node2{
int u,v,w,next;
int vis;
}E2[maxm];
void init(){
memset(head,-1,sizeof(head));tot=0;
}
void addedge(int u,int v,int w){
E2[tot].vis=0;
E2[tot].u=u,E2[tot].v=v,E2[tot].w=w,E2[tot].next=head[u],head[u]=tot++;
}
int Find(int x){
if(x==fa[x]) return x;
return fa[x]=Find(fa[x]);
}
void Kruskal(){
for(int i=1; i<=n; i++) fa[i]=i;
mst=0;int num=0;
for(int i=1; i<=m; i++){
int u=E1[i].u,v=E1[i].v;
int fx=Find(u),fy=Find(v);
if(fx!=fy){
fa[fx]=fy;
addedge(u,v,E1[i].w);
addedge(v,u,E1[i].w);
num++;
E1[i].vis=1;
mst+=E1[i].w;
}
if(num==n-1) break;
}
}
//处理dep
void dfs1(int u,int f,int d)
{
dep[u]=d;
for(int i=head[u]; ~i; i=E2[i].next){
int v=E2[i].v;
if(v==f) continue ;
dfs1(v,u,d+1);
}
}
//暴力LCA
void dfs2(int u,int v,int w)
{
if(u==v) return ;
if(dep[u]<dep[v]) swap(u,v);
for(int i=head[u]; ~i; i=E2[i].next){
int to=E2[i].v;
if(to==u) return ;
if(dep[u]<dep[to]) continue;
if(E2[i].w==w){
E2[i].vis=E2[i^1].vis=1;
}
dfs2(to,v,w);
}
}
int main()
{
int u,v,w;
while(scanf("%d%d",&n,&m)!=EOF)
{
for(int i=1; i<=m; i++){
scanf("%d%d%d",&u,&v,&w);
E1[i].u=u,E1[i].v=v,E1[i].w=w,E1[i].vis=0;
}
init();
sort(E1+1,E1+m+1);
Kruskal();
dfs1(1,0,1);
//枚举非树边
for(int i=1; i<=m; i++){
if(!E1[i].vis){
dfs2(E1[i].u,E1[i].v,E1[i].w);
}
}
int ans=0;
for(int i=0; i<tot; i+=2){
if(E2[i].vis){
mst-=E2[i].w;
}
else{
ans++;
}
}
printf("%d %d\n",ans,mst);
}
return 0;
}