![在这里插入图片描述](https://img-blog.csdnimg.cn/20190730200810230.png)
题目分析:
- 不同的最小生成树中相应权值边的个数是相等的
- 先跑一遍
K
r
u
s
k
a
l
Kruskal
Kruskal,将相同权值的边放在一个块里,并且统计在最小生成树中有当前权值的边多少条
-
d
f
s
dfs
dfs统计答案,对于每一种权值的边,遍历每个块中的边,选或不选,若选入的边等于最小生成树中该权值边的条数,
+
+
s
u
m
++sum
++sum,再根据乘法原理统计答案
- 注意并查集不能路径压缩,不然回溯时回不去
Code
#include <bits/stdc++.h>
using namespace std;
#define mod 31011
#define maxn 110
#define maxm 1010
int cnt=0,n,m,sum=0,f[maxn],ans=1;
struct edge {
int u,v,w;
}e[maxm];
struct node {
int st,ed,w;
}ne[maxm];
inline void init_() {
freopen("a.txt","r",stdin);
}
inline int read_() {
int x=0,f=1;
char c=getchar();
while(c<'0'||c>'9') {
if(c=='-') f=-1;
c=getchar();
}
while(c>='0'&&c<='9') {
x=(x<<3)+(x<<1)+c-'0';
c=getchar();
}
return x*f;
}
inline void clean_() {
for(int i=1;i<=n;++i) f[i]=i;
memset(ne,0,sizeof(ne));
}
int find_(int x) {
if(f[x]==x) return x;
else return find_(f[x]);
}
inline bool cmp_(edge aa,edge bb) {
return aa.w<bb.w;
}
inline void kruskal_() {
sort(e+1,e+m+1,cmp_);
int tot=0;
for(int i=1;i<=m;++i) {
if(e[i].w!=e[i-1].w) {
ne[++cnt].st=i;
ne[cnt-1].ed=i-1;
}
int fx=find_(e[i].u),fy=find_(e[i].v);
if(fx!=fy) {
f[fx]=fy;
++tot;
++ne[cnt].w;
}
}
ne[cnt].ed=m;
if(tot<n-1) {
printf("0");
exit(0);
}
}
void dfs_(int k,int x,int t) {
if(x==ne[k].ed+1) {
if(t==ne[k].w) ++sum;
return;
}
int fx=find_(e[x].u),fy=find_(e[x].v);
if(fx!=fy) {
f[fx]=fy;
dfs_(k,x+1,t+1);
f[fx]=fx;f[fy]=fy;
}
dfs_(k,x+1,t);
}
void readda_() {
n=read_();m=read_();
clean_();
for(int i=1;i<=m;++i) {
e[i].u=read_();e[i].v=read_();e[i].w=read_();
}
kruskal_();
for(int i=1;i<=n;++i) f[i]=i;
for(int i=1;i<=cnt;++i) {
sum=0;
dfs_(i,ne[i].st,0);
ans=(ans*sum)%mod;
for(int j=ne[i].st;j<=ne[i].ed;++j) {
int fx=find_(e[j].u),fy=find_(e[j].v);
if(fx!=fy) f[fx]=fy;
}
}
printf("%d",ans%mod);
}
int main() {
readda_();
return 0;
}