HDU 4408 最小生成樹計數
阿新 • • 發佈:2018-12-11
思路:首先需要了解Matrix_Tree定理
1.設 G是無向圖的鄰接表,D是無向圖各個點的度數
2.令M=D-G,則M的任意n-1階餘子式的行列式值的 絕對值, 就是無向圖生成樹的個數
應注意的是鄰接表的值不只是0,1值,也就是說,當圖出現重邊的話,鄰接表該位置累加。
這道題思路,對於最小生成樹,克魯斯卡爾演算法是將邊從小到大排序,對於同一大小的邊,
在最小生成樹中用到的數量是一定的(這個需要好好理解),這樣的話,我們每次只需要將滿足條件的
同樣大小的邊統計一下,這樣會產生多個聯通分量,對每個聯通分量 用Matrix_Tree計數。
生成樹的點是之前每個聯通分量的縮點,固需要 兩個並查集。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int N=110; int n, m, mo, fa[N], ka[N], lin[N][N]; ll ans, M[N][N]; bitset<N> vis; struct Edge{ int u, v, w; bool operator <(const Edge &b) const{ return w<b.w; } }e[N*10]; void init(){ ans=1; for(int i=1; i<=n; i++){ fa[i]=ka[i]=i; } } int _find(int f[], int r){ return f[r]==r? r:f[r]=_find(f, f[r]); } /*行列式求值模板,將行列式化成上三角行列式,對角線乘積就是答案, 複雜度O(n^3)多一點*/ ll det(int n){ for(int i=0; i<n; i++) for(int j=0; j<n; j++) M[i][j]%=mo; ll ret=1; for(int i=1; i<n; i++){ for(int j=i+1; j<n; j++) while(M[j][i]){ ll t=M[i][i]/M[j][i]; for(int k=i; k<n; k++) M[i][k]=(M[i][k]-t*M[j][k])%mo; for(int k=i; k<n; k++) swap(M[i][k], M[j][k]); ret=-ret; } if(M[i][i]==0) return 0ll; ret=ret*M[i][i]%mo; } return (ret+mo)%mo; } /***********************************/ void Matrix_Tree(){ vector<int> G[N]; for(int i=1; i<=n; i++)if(vis[i]){//劃分連通分量 G[_find(ka, i)].push_back(i); vis[i]=0; } for(int i=1; i<=n; i++)if(G[i].size()){ for(int a=0; a<n; a++) for(int b=0; b<n; b++) M[a][b]=0; int len=G[i].size(); for(int j=0; j<len; j++){ for(int k=j+1; k<len; k++){ int u=G[i][j], v=G[i][k]; M[j][j]+=lin[u][v]; M[k][k]+=lin[u][v]; M[k][j]-=lin[u][v]; M[j][k]=M[k][j]; } } ans=(ans*det(len))%mo; } for(int i=1; i<=n; i++) fa[i]=_find(ka, i); } int main(){ while(~scanf("%d%d%d", &n, &m, &mo)&&(n||m||mo)){ for(int i=1; i<=m; i++) scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w); init(); sort(e+1, e+1+m); int pre=e[1].w; vis.reset(); memset(lin, 0, sizeof lin); for(int i=1; i<=m; i++){ int u=e[i].u, v=e[i].v; int tu=_find(fa, u), tv=_find(fa, v); if(tu!=tv){ vis[tu]=1; vis[tv]=1; ka[_find(ka, tu)]=_find(ka, tv); lin[tu][tv]++; lin[tv][tu]++; } if(i==m || pre!=e[i+1].w){ Matrix_Tree(); pre=e[i+1].w; } } bool ok=true; for(int i=2; i<=n; i++) if(fa[i]!=fa[i-1]){ ok=false; break; } if(!ok) printf("0\n"); else printf("%lld\n", (ans+mo)%mo); } return 0; } /* 6 6 100 1 2 1 2 3 1 3 4 1 4 1 1 4 5 2 5 6 3 */