题目传送门
题目大意: 求一张图有多少棵最小生成树。
最小生成树有个性质,就是每种长度的边的数量是固定的。
所以先随便找一棵最小生成树,考虑长度为 i i i 的边,先将长度不为 i i i 的最小生成树中的边加入进去,然后就形成了若干个连通块,然后剩下的问题就是用长度为 i i i 的边将这些连通块连成一棵树有多少种方案,显然用矩阵树定理就能求。
最后将每种长度的方案数乘起来就是答案了。
代码如下:
#include
#include
#include
using namespace std;
#define maxn 110
#define mod 31011
int n,m,ans=1;
struct edge{int x,y,z;}e[1010];
int belong[maxn];bool v[1010];
struct BCJ{
int fa[maxn];
void init(){for(int i=1;i<=n;i++)fa[i]=i;}
int findfa(int x){return x==fa[x]?x:fa[x]=findfa(fa[x]);}
bool linked(int x,int y){return findfa(x)==findfa(y);}
bool link(int x,int y)
{
x=findfa(x),y=findfa(y);
if(x==y)return false;
fa[y]=x;return true;
}
}F;
bool cmp(edge x,edge y){return x.z<y.z;}
int dec(int x){return x<0?x+mod:x;}
int f[maxn][maxn];int det(int l)
{
int re=1,fu=1;
for(int i=1,p;i<=l;i++)
{
for(int j=i+1;j<=l;j++)
while(f[j][i]!=0){
p=f[i][i]/f[j][i];
for(int k=i;k<=l;k++)f[i][k]=f[i][k]-f[j][k]*p;
swap(f[i],f[j]); fu=-fu;
}
re=1ll*re*f[i][i]%mod;
}
return re*fu;
}
int main()
{
scanf("%d %d",&n,&m);for(int i=1;i<=m;i++)
scanf("%d %d %d",&e[i].x,&e[i].y,&e[i].z);sort(e+1,e+m+1,cmp);
F.init();for(int i=1;i<=m;i++)if(F.link(e[i].x,e[i].y))v[i]=true;
for(int i=2;i<=n;i++)if(F.findfa(i)!=F.findfa(1))return printf("0"),0;
int now=1;while(now<=m)
{
bool con=false;int to=now;for(;e[to].z==e[now].z&&to<=m;to++)con|=v[to];if(!con){now=to;continue;}
F.init();for(int i=1;i<=m;i++)if(v[i]&&e[i].z!=e[now].z)F.link(e[i].x,e[i].y);
int id=0;memset(belong,0,sizeof(belong));memset(f,0,sizeof(f));
for(int i=1;i<=n;i++)if(F.fa[i]==i)belong[i]=++id;
for(int i=1;i<=n;i++)belong[i]=belong[F.findfa(i)];
for(int i=now;i<to;i++){
int x=belong[e[i].x],y=belong[e[i].y];
f[x][y]--,f[y][x]--,f[x][x]++,f[y][y]++;
}
ans=1ll*ans*det(id-1)%mod;now=to;
}
printf("%d",ans);
}