luogu P4208 [JSOI2008]最小生成树计数
题面传送门
这个东西肯定有一个性质。
那么这个性质就是对于每个最小生成树同种权值的边所联通的点其实是一样的。
有了这个结论就很好做了,枚举每个权值然后其它树边缩点,然后跑矩阵树定理即可。
时间复杂度\(O(nm+n^3)\)
code:
#include<bits/stdc++.h>
#define I inline
#define max(a,b) ((a)>(b)?(a):(b))
#define min(a,b) ((a)<(b)?(a):(b))
#define abs(x) ((x)>0?(x):-(x))
#define re register
#define ll long long
#define db double
#define N 100
#define M 1000
#define mod 31011
#define eps (1e-7)
#define U unsigned int
#define it iterator
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
using namespace std;
int n,m,un,wn,A[N+5][N+5],Fl[N+5],ans=1,fa[N+5],now,pus,cnt,in[M+5];
struct ques{int x,y,z;}S[M+5],tmp;I bool cmp(ques x,ques y){return x.z<y.z;}
I int Getfa(int x){return x==fa[x]?x:fa[x]=Getfa(fa[x]);} vector<ques> G[N+5];
I void swap(int &x,int &y){x^=y^=x^=y;}
I void calc(){
re int i,j,h;for(i=2;i<=pus;i++){
for(j=i+1;j<=pus;j++){
while(A[j][i]){
now=mod-A[i][i]/A[j][i];for(h=i;h<=pus;h++)A[i][h]=(A[i][h]+A[j][h]*now)%mod,swap(A[i][h],A[j][h]);ans*=-1;
}
}
}for(i=2;i<=pus;i++) ans=ans*A[i][i]%mod;
}
I void merge(int x,int y){un=Getfa(x);wn=Getfa(y);un^wn&&(fa[un]=wn);}
I void insert(int x,int y){A[x][x]++;A[y][y]++;A[x][y]--;A[y][x]--;}
int main(){
freopen("1.in","r",stdin);
re int i,j,h;scanf("%d%d",&n,&m);for(i=1;i<=m;i++) scanf("%d%d%d",&S[i].x,&S[i].y,&S[i].z);for(i=1;i<=n;i++) fa[i]=i;sort(S+1,S+m+1,cmp);
for(i=1;i<=m;i++){
un=Getfa(S[i].x);wn=Getfa(S[i].y);un^wn&&(fa[un]=wn,cnt++,in[i]=1);if(cnt==n-1) {now=S[i].z;break;}
}if(cnt<n-1){printf("0\n");return 0;}while(S[m].z^now) m--;cnt=0;
for(i=1;i<=m;i++)G[(S[i].z==S[i-1].z)?cnt:++cnt].push_back(S[i]);
for(i=1;i<=cnt;i++){
now=G[i][0].z;for(j=1;j<=n;j++) fa[j]=j;for(j=1;j<=m;j++) in[j]&&S[j].z^now&&(merge(S[j].x,S[j].y),0);
pus=0;for(j=1;j<=n;j++) (Getfa(j)==j)&&(Fl[j]=++pus);for(j=0;j<G[i].size();j++) tmp=G[i][j],un=Getfa(tmp.x),wn=Getfa(tmp.y),wn^un&&(insert(Fl[un],Fl[wn]),0);
for(j=1;j<=pus;j++) for(h=1;h<=pus;h++) A[j][h]=(A[j][h]%mod+mod)%mod;calc();for(j=1;j<=pus;j++)Me(A[j],0);
}printf("%d\n",(ans+mod)%mod);
}