题目大意
给定n个点m条无向边的图G,求G的最小生成树个数,模31011。
其实有个特殊条件但我们的高端做法可以忽略。
n<=100,m<=1000
矩阵树定理
我们来思考一个图G所有最小生成树的同一个性质:
假如我加入所有边权<=w的边,任何生成树的联通情况一定是一致的。
脑补得证QAQ
或者你考虑反证+切割引理。
我们把边权相同的边当做一组边,每次在原来的基础上把这样边都加入。例如原本三个在
<w
<script type="math/tex" id="MathJax-Element-107">
但是模数不是质数,让我们消元很头痛,有个精妙的方法。
那就是例如i和j两行,要将第j行消掉,我们可以辗转相消(相当于对(i,i)和(j,i)两项做gcd的过程)!详见代码。
然后这样的复杂度是多少我不太会分析,一个十分松的上界是n^4。
#include<cstdio>
#include<algorithm>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=100+10,maxm=1000+10,mo=31011;
struct dong{
int u,v,l;
} edge[maxm];
int c[maxn][maxn],cr[maxn][maxn],d[maxn][maxn],a[maxn][maxn],dis[maxn][maxn];
int fa[maxn],st[maxn],rj[maxn],bh[maxn],hb[maxn],ds[maxn];
int i,j,k,s,l,r,t,n,m,tot,top,ans,root;
bool czy;
bool cmp(dong a,dong b){
return a.l<b.l;
}
bool cmp2(int a,int b){
return rj[a]<rj[b];
}
int getfa(int x){
return fa[x]?fa[x]=getfa(fa[x]):x;
}
int det(){
int n=t-1;
int cnt=1;
int i,j,k,t;
fo(i,1,n)
fo(j,1,n)
cr[i][j]%=mo;
fo(i,1,n){
fo(j,i+1,n)
while (cr[j][i]){
t=cr[i][i]/cr[j][i];
fo(k,i,n)
cr[i][k]-=cr[j][k]*t;
fo(k,i,n) swap(cr[i][k],cr[j][k]);
cnt=-cnt;
}
if (!cr[i][i]) return 0;
cnt=cnt*cr[i][i]%mo;
}
return cnt;
}
int main(){
scanf("%d%d",&n,&m);
fo(i,1,m){
scanf("%d%d%d",&j,&k,&l);
edge[i].u=j;edge[i].v=k;
edge[i].l=l;
}
sort(edge+1,edge+m+1,cmp);
l=1;
ans=1;
while (l<=m){
r=l;
while (r<m&&edge[r+1].l==edge[l].l) r++;
top=0;
fo(i,1,n) bh[i]=0;
fo(i,1,n)
if (!fa[i]) st[++top]=i,bh[i]=top;
fo(i,1,top) ds[i]=0;
fo(i,1,top)
fo(j,1,top)
dis[i][j]=0;
fo(i,l,r){
j=getfa(edge[i].u);k=getfa(edge[i].v);
if (j==k) continue;
ds[bh[j]]++;ds[bh[k]]++;
dis[bh[j]][bh[k]]++;
dis[bh[k]][bh[j]]++;
}
fo(i,l,r){
if (getfa(edge[i].u)!=getfa(edge[i].v)){
fa[getfa(edge[i].u)]=getfa(edge[i].v);
}
}
fo(i,1,top) rj[st[i]]=getfa(st[i]);
sort(st+1,st+top+1,cmp2);
i=1;
while (i<=top){
j=i;
while (j<top&&rj[st[j+1]]==rj[st[i]]) j++;
if (i==j){
i++;
continue;
}
t=0;
fo(k,i,j) hb[++t]=bh[st[k]];
fo(k,1,t)
fo(s,1,t)
if (k!=s) d[k][s]=0;else d[k][s]=ds[hb[k]];
fo(k,1,t)
fo(s,1,t)
a[k][s]=dis[hb[k]][hb[s]];
fo(k,1,t)
fo(s,1,t)
c[k][s]=d[k][s]-a[k][s];
fo(k,1,t-1)
fo(s,1,t-1)
cr[k][s]=c[k+1][s+1];
ans=ans*det()%mo;
i=j+1;
}
l=r+1;
}
czy=1;
root=0;
fo(i,1,n)
if (!fa[i]){
if (!root) root=i;
else{
czy=0;
break;
}
}
(ans+=mo)%=mo;
if (czy) printf("%d\n",ans);else printf("0\n");
}