题目链接:https://ac.nowcoder.com/acm/contest/272/D
思路和代码参考于:https://blog.csdn.net/qq_37025443/article/details/84747158
看着官方答案也写不出来,只好参考了大神的代码,还是我自己太菜了。
仔细看题意和样例可以知道,如果没有权值相同的边,那么答案明显就是最小生成树中桥的个数,克鲁斯卡尔+Tarjan就可以解决;当有相同权值边时,上面方法就不好处理了,最小生成树不唯一。所以,对于所有最小生成树上的边,我们都加到一个新图中去,如果有重边,在加入新图中的同时,以重边的一个顶点为起点,Tarjan一遍我们的新图(对于每个重边,如果顶点属于不同的连通分量,都要Tarjan),标记所有为桥的边,并去除新图。最后,被标记为桥的边就一定是在最小生成树上的边。
细节见代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+100;
const int INF=1<<30;
struct edge{
int fr,to,nxt;
ll w;
int id;
bool operator < (const edge &a) const {
return w<a.w;
}
};
int pre[N],n,head[N],cnt;
edge e[N*2];
edge f[N*2];
bool is_cut[N];
int father[N];
int low[N],dfn[N];
int tim=0;
int ans[N];
int visit[N];
void add1(int fr,int to,ll w){
e[cnt].fr=fr;
e[cnt].to=to;
e[cnt].w=w;
cnt++;
}
int fcnt;
void add2(int fr,int to,int id){
f[fcnt].fr=fr;
f[fcnt].to=to;
f[fcnt].id=id;
f[fcnt].nxt=head[fr];
head[fr]=fcnt++;
}
int fin(int x){
if(x==pre[x])
return x;
return pre[x]=fin(pre[x]);
}
//tarjan算法寻找桥
void tarjan(int x, int fa) {
dfn[x] = low[x] = ++tim;
for(int i = head[x], to, id; i!=-1; i=f[i].nxt) {
if((id = f[i].id) == fa) continue;
to = f[i].to;
if(!dfn[to]) {
tarjan(to, id), low[x] = min(low[x], low[to]);
if(low[to] > dfn[x]) ans[id] = 1;
}
else low[x] = min(low[x], dfn[to]);
}
}
/*
如果没有重边,答案就是最小生成树的边的个数(所有的边都是桥)
如果有重边,将所有的边(当然是最小生成树的重边)都加到新图中去,tarjan求桥,求出来的
桥的个数就是答案
*/
void Kruskal(){
fcnt=0;
memset(head,-1,sizeof(head));
//贪心+并查集
for(int i=1;i<=n;++i)
pre[i]=i;
sort(e,e+cnt);
for(int i=0;i<cnt;++i){
int u=fin(e[i].fr);
int v=fin(e[i].to);
if(u!=v){
//在新图中加入最小生成树的边
add2(u,v,i);
add2(v,u,i);
}
int j=i+1;
//有边权相同的边
while(j<cnt&&e[j-1].w==e[j].w)
{
u=fin(e[j].fr);
v=fin(e[j].to);
if(u!=v){
//如果不是一个联通分量的,加入新图,满足克鲁斯卡尔的思想
add2(u,v,j);
add2(v,u,j);
}
j++;
}
for(int w=i;w<j;w++)
{
u=fin(e[w].fr);
v=fin(e[w].to);
//寻找桥
if(u!=v&&!dfn[u]){
tarjan(u,-1);
}
}
//删除新图,以免影响之后的判断
for(;i<j;i++)
{
u=fin(e[i].fr);
v=fin(e[i].to);
if(u!=v){
dfn[u]=dfn[v]=0;
low[u]=low[v]=0;
head[u]=head[v]=-1;
pre[v]=u;
}
}
fcnt=0;
i--;
}
}
int p;
void _count()
{
ll res=0;
for(int i=0;i<cnt;i++)
{
if(ans[i]) res++;
}
printf("%lld\n",res);
}
int main(){
int a,b,c;
int m;
scanf("%d%d%d",&n,&m,&p);
while(m--){
scanf("%d%d%d",&a,&b,&c);
add1(a,b,c);
}
Kruskal();
memset(dfn,0,sizeof(dfn));
memset(father,0,sizeof(father));
memset(low,0,sizeof(low));
memset(is_cut,false,sizeof(is_cut));
tim=0;
_count();
}