【Matrix-tree定理】【并查集】【kruscal算法】bzoj1016 [JSOI2008]最小生成树计数
2017-10-06 01:05
597 查看
题意:求一个图的最小生成树个数。
矩阵树定理:一张无向图的生成树个数 = (度数矩阵 - 邻接矩阵)的任意一个n-1主子式的值。
度数矩阵除了对角线上D[i][i]为i的度数(不计自环)外,其他位置是0。
邻接矩阵G[i][j]的值为i与j之间的边数(重边要记入)。
一个定理:一个图的所有MST中,相同权值的边数肯定是相等的。
于是将边从小到大排序之后,根据权值划分阶段,将之前的点全缩点,这一阶段的边中仅考虑当前权值的边,然后把图划分成多个连通块,对每个连通块使用矩阵树定理求生成树个数,该阶段的值就是所有连通块乘起来。然后最终答案就是所有阶段的值乘起来。
缩点使用了动态维护并查集的方法。
无法求出生成树的情况要特判。
md另外这题模的数是合数,所以高斯消元贼恶心……用了特殊的处理方法
矩阵树定理:一张无向图的生成树个数 = (度数矩阵 - 邻接矩阵)的任意一个n-1主子式的值。
度数矩阵除了对角线上D[i][i]为i的度数(不计自环)外,其他位置是0。
邻接矩阵G[i][j]的值为i与j之间的边数(重边要记入)。
一个定理:一个图的所有MST中,相同权值的边数肯定是相等的。
于是将边从小到大排序之后,根据权值划分阶段,将之前的点全缩点,这一阶段的边中仅考虑当前权值的边,然后把图划分成多个连通块,对每个连通块使用矩阵树定理求生成树个数,该阶段的值就是所有连通块乘起来。然后最终答案就是所有阶段的值乘起来。
缩点使用了动态维护并查集的方法。
无法求出生成树的情况要特判。
md另外这题模的数是合数,所以高斯消元贼恶心……用了特殊的处理方法
#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; }
相关文章推荐
- 【BZOJ 1016】 [JSOI2008]最小生成树计数(matrix-tree定理做法)
- 【BZOJ1016】【JSOI2008】最小生成树计数 & 【BZOJ1543】生成树计数 (kruskal+matrix_tree定理)
- [bzoj1016][JSOI2008]最小生成树计数 (Kruskal + Matrix Tree 定理)
- [矩阵树定理 模板题] BZOJ 1016 [JSOI2008]最小生成树计数 & HDU 4408 Minimum Spanning Tree
- BZOJ 题目1016: [JSOI2008]最小生成树计数(Kruskal+Matrix_Tree)
- [BZOJ1016][JSOI2008]最小生成树计数-并查集-状态压缩-最小生成树
- BZOJ1016([JSOI2008]最小生成树计数)Kruskal+Matrix_Tree定理
- 【BZOJ 1016】 1016: [JSOI2008]最小生成树计数 (DFS|矩阵树定理)
- BZOJ.1016.[JSOI2008]最小生成树计数(Matrix Tree定理 Kruskal)
- BZOJ 1016 [JSOI2008]最小生成树计数 ——Matrix-Tree定理
- BZOJ 1016 [JSOI2008]最小生成树计数 Kruskal Matrix-Tree定理
- [生成树 MatrixTree定理] BZOJ 1016 [JSOI2008]最小生成树计数
- 【BZOJ 1016】[JSOI2008]最小生成树计数 基尔霍夫矩阵||暴力
- 【BZOJ】【1016】【JSOI2008】最小生成树计数
- BZOJ 1016 JSOI 2008 最小生成树计数 Kruskal+搜索
- Bzoj1016: [JSOI2008]最小生成树计数
- BZOJ 1016: [JSOI2008]最小生成树计数
- bzoj1016:[JSOI2008]最小生成树计数
- [bzoj1016][JSOI2008]最小生成树计数【MST】【暴力】
- bzoj1016 [JSOI2008]最小生成树计数(kruskal+dfs+乘法原理)