最小生成树计数 模板 hdu 4408
题意是给定n个点,m条边的无向图,求最小生成树的个数对p取模。
用kruscal计算最小生成树时,每次取连接了两个不同联通块的最小的边。也就是先处理d1条c1长度的边,再处理d2条c2长度的边。长度相同的边无论怎么选,最大联通情况都是固定的。 分别对ci长度的边产生的几个联通块计算生成树数量再乘起来,然后把这些联通块缩点,再计算ci+1长度的边。
生成树计数用Matrix-Tree定理,上一篇是无重边的,这题的缩点后是会产生重边的,Matrix-tree也适用: //抄别人博客的
Kirchhoff矩阵任意n-1阶子矩阵的行列式的绝对值就是无向图的生成树的数量。
Kirchhoff矩阵的定义是度数矩阵-邻接矩阵。
1、G的度数矩阵D[G]:n*n的矩阵,Dii等于Vi的度数,其余为0。
2、G的邻接矩阵A[G]:n*n的矩阵, Vi、Vj之间有边直接相连,则 Aij=ij之间的边数,否则为0。
并查集fa[i]是当前长度之前,节点所属的联通块,ka[i]是当前长度的边连接后它在的联通块。
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #include <vector> 5 using namespace std; 6 typedef long long ll; 7 const int N=101; 8 const int M=1001; 9 ll n,m,p,ans; 10 vector<int>gra[N]; 11 struct edge{ 12 int u,v,w; 13 }e[M]; 14 int cmp(edge a,edge b){ 15 return a.w<b.w; 16 } 17 ll mat[N][N],g[N][N]; 18 ll fa[N],ka[N],vis[N]; 19 ll det(ll c[][N],ll n){ 20 ll i,j,k,t,ret=1; 21 for(i=0;i<n;i++) 22 for(j=0;j<n;j++) c[i][j]%=p; 23 for(i=0; i<n; i++){ 24 for(j=i+1; j<n; j++) 25 while(c[j][i]){ 26 t=c[i][i]/c[j][i]; 27 for(k=i; k<n; k++) 28 c[i][k]=(c[i][k]-c[j][k]*t)%p; 29 swap(c[i],c[j]); 30 ret=-ret; 31 } 32 if(c[i][i]==0) 33 return 0L; 34 ret=ret*c[i][i]%p; 35 } 36 return (ret+p)%p; 37 } 38 ll find(ll a,ll f[]){ 39 return f[a]==a?a:find(f[a],f); 40 } 41 void matrix_tree(){//对当前长度的边连接的每个联通块计算生成树个数 42 for(int i=0;i<n;i++)if(vis[i]){//当前长度的边连接了i节点 43 gra[find(i,ka)].push_back(i);//将i节点压入所属的联通块 44 vis[i]=0;//一边清空vis数组 45 } 46 for(int i=0;i<n;i++) 47 if(gra[i].size()>1){//联通块的点数为1时生成树数量是1 48 memset(mat,0,sizeof mat);//清空矩阵 49 int len=gra[i].size(); 50 for(int j=0;j<len;j++) 51 for(int k=j+1;k<len;k++){//构造这个联通块的矩阵(有重边) 52 int u=gra[i][j],v=gra[i][k]; 53 if(g[u][v]){ 54 mat[k][j]=(mat[j][k]-=g[u][v]); 55 mat[k][k]+=g[u][v];mat[j][j]+=g[u][v]; 56 } 57 } 58 ans=ans*det(mat,gra[i].size()-1)%p; 59 for(int j=0;j<len;j++)fa[gra[i][j]]=i;//缩点 60 } 61 for(int i=0;i<n;i++) 62 { 63 gra[i].clear(); 64 ka[i]=fa[i]=find(i,fa); 65 } 66 } 67 int main(){ 68 while(scanf("%lld%lld%lld",&n,&m,&p),n){ 69 for(int i=0;i<m;i++){ 70 int u,v,w; 71 scanf("%d%d%d",&u,&v,&w); 72 u--;v--; 73 e[i]=(edge){u,v,w}; 74 } 75 sort(e,e+m,cmp); 76 memset(g,0,sizeof g); 77 ans=1; 78 for(ll i=0;i<n;i++)ka[i]=fa[i]=i; 79 for(ll i=0;i<=m;i++){//边从小到大加入 80 if(i&&e[i].w!=e[i-1].w||i==m)//处理完长度为e[i-1].w的所有边 81 matrix_tree();//计算生成树 82 ll u=find(e[i].u,fa),v=find(e[i].v,fa);//连的两个缩点后的点 83 if(u!=v)//如果不是一个 84 { 85 vis[v]=vis[u]=1; 86 ka[find(u,ka)]=find(v,ka);//两个分量在一个联通块里。 87 g[u][v]++,g[v][u]++;//邻接矩阵 88 } 89 } 90 int flag=1; 91 for(int i=1;i<n;i++)if(fa[i]!=fa[i-1])flag=0; 92 printf("%lld\n",flag?ans%p:0);//注意p可能为1,这样m=0时如果ans不%p就会输出1 93 } 94 }