bzoj1016 [JSOI2008]最小生成树计数——Kruskal+矩阵树定理
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1016
从 Kruskal 算法的过程来考虑产生多种方案的原因,就是边权相同的边有一样的功能,从而带来了多种选择;
对于每一层次(边权相同)的边来说,它们最终会把图进一步连通;
所以在这一层之前缩好点,看看这一层连接出几个新连通块,对于每个连通块内部做矩阵树定理求生成树个数,再乘法原理乘起来即可;
注意高斯消元的矩阵不能直接用原图的点标号等,求行列式会出错;
疑惑:以及高斯消元 return 时为什么要加个 abs?
代码如下:
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<vector> #include<cmath> using namespace std; vector<int>v[105]; int n,m,fa[105],pa[105],a[105][105],c[105][105],ans=1,mod=31011; bool vis[105]; struct N{ int hd,to,w; N(int h=0,int t=0,int w=0):hd(h),to(t),w(w) {} }edge[1005]; bool cmp(N x,N y){return x.w<y.w;} int find(int x,int f[105]){return f[x]==x?x:find(f[x],f);}// int gauss(int n) { for(int i=1;i<=n;i++) for(int j=1;j<=n;j++) a[i][j]%=mod;// int fl=0,ret=1; for(int i=1;i<=n;i++) { int t=i; for(int j=i+1;j<=n;j++) if(abs(a[j][i])>abs(a[t][i]))t=j;//abs if(t!=i) { fl^=1; for(int j=i;j<=n;j++)swap(a[i][j],a[t][j]); } 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++) { int tp=a[i][k]; a[i][k]=a[j][k];//a=b a[j][k]=(tp-a[j][k]*tmp)%mod;//b=a%b } fl^=1; } (ret*=a[i][i])%=mod; } return (abs(ret)%mod+mod)%mod;//abs!? // return ret; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)fa[i]=i; for(int i=1,x,y,z;i<=m;i++) scanf("%d%d%d",&edge[i].hd,&edge[i].to,&edge[i].w); sort(edge+1,edge+m+1,cmp); for(int i=1;i<=m+1;i++) { if(edge[i].w!=edge[i-1].w || i==m+1) { for(int j=1;j<=n;j++) { if(!vis[j])continue; int f1=find(j,pa); v[f1].push_back(j);//v是点的集合 vis[j]=0; } for(int j=1;j<=n;j++) { if(v[j].size()<=1)continue; memset(a,0,sizeof a); int siz=v[j].size(); for(int k=0;k<siz;k++) for(int l=k+1;l<siz;l++) { int x=v[j][k],y=v[j][l],t=c[x][y]; // a[x][x]+=t; a[y][y]+=t; // a[x][y]-=t; a[y][x]-=t; a[k+1][k+1]+=t; a[l+1][l+1]+=t; a[k+1][l+1]-=t; a[l+1][k+1]-=t;//! } (ans*=gauss(siz-1))%=mod; // (ans*=gauss(n-1))%=mod; for(int k=0;k<siz;k++)fa[v[j][k]]=j; } for(int j=1;j<=n;j++) { pa[j]=fa[j]=find(j,fa); v[j].clear(); } } int f1=find(edge[i].hd,fa),f2=find(edge[i].to,fa); if(f1==f2)continue; pa[find(f1,pa)]=find(f2,pa); vis[f1]=1; vis[f2]=1; c[f1][f2]++; c[f2][f1]++; } for(int i=2;i<=n;i++)//! if(find(i,fa)!=find(i-1,fa)){printf("0"); return 0;} printf("%d",ans); return 0; }