#include <cstdio>
#include <algorithm>
#define N 105
#define MOD 31011
using namespace std;
struct Edge {
int u,v,c;
Edge() {}
void scan() { scanf("%d%d%d",&u,&v,&c); }
bool operator < (const Edge& rhs) const {
return c<rhs.c;
}
}e[N*10];
int n,m,ans=1,rest,top,_top,pa[N],d[N],s[N],_s[N],f[N],a[N][N],b[N][N];
int root(int x) { return pa[x]==x ? pa[x] : root(pa[x]); }
int det() {
int re=1;
int _n=_top-1;
for(int i=1;i<=_n;++i) {
for(int j=i+1;j<=_n;++j)
while(a[j][i]) {
int tmp=a[i][i]/a[j][i];
for(int k=i;k<=n;++k) a[i][k]-=a[j][k]*tmp;
for(int k=i;k<=n;++k) swap(a[i][k],a[j][k]);
re=-re;
}
if(!a[i][i]) return 0;
re=re*a[i][i]%MOD;
}
return re;
}
inline bool cmp(const int& lhs,const int& rhs) { return pa[lhs]<pa[rhs]; }
int main() {
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i) e[i].scan();
for(int i=1;i<=n;++i) pa[i]=i;
rest=n;
sort(e+1,e+m+1);
for(int l=1,r;l<=m && rest>1;l=r) {
r=l;
while(r<=m && e[l].c==e[r].c) ++r;
for(int i=1;i<=n;++i) f[i]=d[i]=0;
top=0;
for(int i=1;i<=n;++i)
if(pa[i]==i) s[++top]=i, f[i]=top;
for(int i=1;i<=top;++i)
for(int j=1;j<=top;++j)
b[i][j]=0;
for(int i=l;i<r;++i) {
int pa_u=root(e[i].u),pa_v=root(e[i].v);
if(pa_u==pa_v) continue;
++d[f[pa_u]], ++d[f[pa_v]];
++b[f[pa_u]][f[pa_v]], ++b[f[pa_v]][f[pa_u]];
}
for(int i=l;i<r;++i) {
int pa_u=root(e[i].u),pa_v=root(e[i].v);
if(pa_u==pa_v) continue;
pa[pa_u]=pa_v;
--rest;
}
for(int i=1;i<=top;++i) pa[s[i]]=root(s[i]);
sort(s+1,s+top+1,cmp);
for(int _l=1,_r;_l<=top;_l=_r) {
_r=_l;
while(_r<=top && pa[s[_r]]==pa[s[_l]]) ++_r;
if(_l+1==_r) continue;
_top=0;
for(int i=_l;i<_r;++i) _s[++_top]=f[s[i]];
for(int i=1;i<=_top;++i)
for(int j=1;j<=_top;++j) {
a[i][j]=-b[_s[i]][_s[j]];
if(i==j) a[i][j]+=d[_s[i]];
}
ans=ans*det()%MOD;
}
}
if(rest>1) ans=0;
printf("%d\n",ans);
return 0;
}
BZOJ 1016 [JSOI2008]最小生成树计数 Kruskal Matrix-Tree定理
最新推荐文章于 2019-03-28 18:45:00 发布