这道题有两种做法,Matrix-Tree定理的做法当年做轮状病毒的时候就接触过,但理解不能。
于是只好以一个蒟蒻的姿势选择了另一种方法。
首先从最小生成树的特点来看,以及我们通过Kruskal得到最小生成树的过程不难发现一个性质:
如果将这棵最小生成树的一些边权相等的边进行替换而不影响连通情况,那么这就是一颗新的最小生成树。
这个性质正过来是对的,反过来也是对的。
也就是说最小生成树的边权相同的边的个数总是不变的。
于是我们进行枚举就可以了,至于检查连通情况,只要对连通块个数进行统计即可。
-------------------------------------------------------------------------------------------------------------------------------------------
Code:
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int N=110;
const int M=1010;
const int MOD=31011;
int f1[N],f2[N],f[N],n,m,k0,sum;
struct edge{
int u,v,c;
}E[M];
bool use[M];
bool cmp(edge a,edge b){return a.c<b.c;}
int get(int x,int *f){return f[x]==x?x:f[x]=get(f[x],f);}
void dfs(int p,int l,int r){
int ret=0;
if(p==r+1){
for(int i=1;i<=n;i++) f2[i]=f[i];
for(int i=l;i<=r;i++)
if(use[i]){
if(get(E[i].u,f2)!=get(E[i].v,f2))
f2[get(E[i].u,f2)]=get(E[i].v,f2);
else return;
}for(int k=1;k<=n;k++) if(get(k,f2)==k) ret++;
if (ret==k0) sum++;
return;
}
use[p]=0; dfs(p+1,l,r);
use[p]=1; dfs(p+1,l,r);
}
int main(){
// freopen("1016.in","r",stdin);
int l,r; int ans=1;
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++) scanf("%d%d%d",&E[i].u,&E[i].v,&E[i].c);
sort(E+1,E+m+1,cmp);
for(int k=1;k<=n;k++) f[k]=k;
for(int j=1;j<=m;j++) if(get(E[j].u,f)!=get(E[j].v,f)) f[get(E[j].u,f)]=get(E[j].v,f);
k0=0; sum=0;
for(int k=1;k<=n;k++) if(get(k,f)==k) k0++;
if(k0!=1){
printf("0\n");
return 0;
}for(int k=1;k<=n;k++) f[k]=k;
for(int i=1;i<=m;i++){
for(int k=1;k<=n;k++) f1[k]=f[k];
l=i; for(;i<m&&E[i+1].c==E[i].c;i++); r=i;
for(int j=l;j<=r;j++) if(get(E[j].u,f1)!=get(E[j].v,f1)) f1[get(E[j].u,f1)]=get(E[j].v,f1);
k0=0; sum=0;
for(int k=1;k<=n;k++) if(get(k,f1)==k) k0++;
dfs(l,l,r);
ans=(ans*sum)%MOD;
for(int k=1;k<=n;k++) f[k]=f1[k];
}printf("%d\n",ans);
return 0;
}