最小生成树计数 模板 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 }

 

posted @ 2019-10-19 10:31  古比  阅读(183)  评论(0编辑  收藏  举报