标签:pen pac kruscal 并查集 cpp algorithm i+1 除了 back
题意:求一个图的最小生成树个数。
矩阵树定理:一张无向图的生成树个数 = (度数矩阵 - 邻接矩阵)的任意一个n-1主子式的值。
度数矩阵除了对角线上D[i][i]为i的度数(不计自环)外,其他位置是0。
邻接矩阵G[i][j]的值为i与j之间的边数(重边要记入)。
一个定理:一个图的所有MST中,相同权值的边数肯定是相等的。
于是将边从小到大排序之后,根据权值划分阶段,将之前的点全缩点,这一阶段的边中仅考虑当前权值的边,然后把图划分成多个连通块,对每个连通块使用矩阵树定理求生成树个数,该阶段的值就是所有连通块乘起来。然后最终答案就是所有阶段的值乘起来。
缩点使用了动态维护并查集的方法。
无法求出生成树的情况要特判。
#include<cstdio> #include<vector> #include<cstring> #include<algorithm> using namespace std; typedef vector<int>::iterator ITER; vector<int>bel[105]; int v[2005],first[105],next[2005],w[2005],e; void AddEdge(int U,int V,int W){ v[++e]=V; w[e]=W; next[e]=first[U]; first[U]=e; } int fa[105]; int find(int x){ return x==fa[x] ? x : fa[x]=find(fa[x]); } const int MOD=31011; struct Edge{ int u,v,w; Edge(const int &u,const int &v,const int &w){ this->u=u; this->v=v; this->w=w; } Edge(){} void read(){ scanf("%d%d%d",&u,&v,&w); } }es[1005]; bool cmp(const Edge &a,const Edge &b){ return a.w<b.w; } int n,m; bool vis[105]; int num[105],p,now,A[105][105],ans=1; void dfs(int U){ num[U]=++p; vis[U]=1; for(ITER it=bel[U].begin();it!=bel[U].end();++it){ for(int i=first[*it];i;i=next[i]){ int V; if(w[i]==now && (V=find(v[i]))!=U){ // printf("%d %d\n",U,V); if(!vis[V]){ dfs(V); } ++A[num[V]][num[V]]; A[num[U]][num[V]]=(A[num[U]][num[V]]-1+MOD)%MOD; } } } } int N; int guass_jordan() { int res=1; for(int i=1;i<=N;i++){ for (int j=i+1;j<=N;j++){ int a=A[i][i],b=A[j][i]; while(b) { int t=a/b; a%=b; swap(a,b); for(int k=i;k<=N;k++){ A[i][k]=(A[i][k]-A[j][k]*t%MOD+MOD)%MOD; } for(int k=i;k<=N;k++){ swap(A[i][k],A[j][k]); } res=res*(MOD-1)%MOD; } } if(!A[i][i]){ return 0; } res=(res*A[i][i])%MOD; } return res; } int tot; int main(){ // freopen("bzoj1016.in","r",stdin); scanf("%d%d",&n,&m); for(int i=1;i<=n;++i){ fa[i]=i; bel[i].push_back(i); } for(int i=1;i<=m;++i){ es[i].read(); AddEdge(es[i].u,es[i].v,es[i].w); AddEdge(es[i].v,es[i].u,es[i].w); } sort(es+1,es+m+1,cmp); for(int i=1;i<=m;++i){ if(es[i].w!=es[i-1].w){ now=es[i].w; memset(vis,0,sizeof(vis)); for(int j=1;j<=n;++j){ if(j==fa[j] && !vis[j]){ p=0; memset(num,0,sizeof(num)); dfs(j); N=p-1; ans=ans*guass_jordan()%MOD; for(int k=1;k<=p;++k){ for(int l=1;l<=p;++l){ A[k][l]=0; } } } } } int f1=find(es[i].u),f2=find(es[i].v); if(f1!=f2){ fa[f1]=f2; ++tot; for(ITER it=bel[f1].begin();it!=bel[f1].end();++it){ bel[f2].push_back(*it); } bel[f1].clear(); } } printf("%d\n",tot==n-1 ? ans : 0); return 0; }
【Matrix-tree定理】【并查集】【kruscal算法】bzoj1016 [JSOI2008]最小生成树计数
标签:pen pac kruscal 并查集 cpp algorithm i+1 除了 back
原文地址:http://www.cnblogs.com/autsky-jadek/p/7630373.html