最小生成树计数模板及原理
参考于:https://blog.csdn.net/jarily/article/details/8902402
*算法引入:
*给定一个含有N个结点M条边的无向图,求它最小生成树的个数t(G);
*
*算法思想:
*抛开“最小”的限制不看,如果只要求求出所有生成树的个数,是可以利用Matrix-Tree定理解决的;
*Matrix-Tree定理此定理利用图的Kirchhoff矩阵,可以在O(N3)时间内求出生成树的个数;
*
*kruskal算法:
*将图G={V,E}中的所有边按照长度由小到大进行排序,等长的边可以按照任意顺序;
*初始化图G’为{V,Ø},从前向后扫描排序后的边,如果扫描到的边e在G’中连接了两个相异的连通块,则将它插入G’中;
*最后得到的图G’就是图G的最小生成树;
*
*由于kruskal按照任意顺序对等长的边进行排序,则应该将所有长度为L0的边的处理当作一个阶段来整体看待;
*令kruskal处理完这一个阶段后得到的图为G0,如果按照不同的顺序对等长的边进行排序,得到的G0也是不同;
*虽然G0可以随排序方式的不同而不同,但它们的连通性都是一样的,都和F0的连通性相同(F0表示插入所有长度为L0的边后形成的图);
*
*在kruskal算法中的任意时刻,并不需要关注G’的具体形态,而只要关注各个点的连通性如何(一般是用并查集表示);
*所以只要在扫描进行完第一阶段后点的连通性和F0相同,且是通过最小代价到达这一状态的,接下去都能找到最小生成树;
*
*经过上面的分析,可以看出第一个阶段和后面的工作是完全独立的;
*第一阶段需要完成的任务是使G0的连通性和F0一样,且只能使用最小的代价;
*计算出这一阶段的方案数,再乘上完成后续事情的方案数,就是最终答案;
*
*由于在第一个阶段中,选出的边数是一定的,所有边的长又都为L0;
*所以无论第一个阶段如何进行代价都是一样的,那么只需要计算方案数就行了;
*此时Matrix-Tree定理就可以派上用场了,只需对F0中的每一个连通块求生成树个数再相乘即可;
*
*Matrix-Tree定理:
*G的所有不同的生成树的个数等于其Kirchhoff矩阵C[G]任何一个n-1阶主子式的行列式的绝对值;
*n-1阶主子式就是对于r(1≤r≤n),将C[G]的第r行,第r列同时去掉后得到的新矩阵,用Cr[G]表示;
*
*算法举例:
*HDU4408(Minimum Spanning Tree)
*
*题目地址:
*http://acm.hdu.edu.cn/showproblem.php?pid=4408
*
*题目大意:
*给定一个含有N个结点M条边的无向图,求它最小生成树的个数,所得结果对p取模;
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<queue>
#include<algorithm>
#include<vector>
using namespace std;const int N=111;
const int M=1111;typedef __int64 LL;struct Edges
{int a,b,c;bool operator<(const Edges & x)const{return c<x.c;}
} edge[M];int n,m;
int mod;
LL f[N],U[N],vist[N];//f,U都是并查集,U是每组边临时使用
LL G[N][N],C[N][N];//G顶点之间的关系,C为生成树计数用的Kirchhoff矩阵vector<int>V[N];//记录每个连通分量int Find(int x,LL f[])
{if(x==f[x])return x;elsereturn Find(f[x],f);
}LL det(LL a[][N],int n)//生成树计数:Matrix-Tree定理
{for(int i=0; i<n; i++)for(int j=0; j<n; j++)a[i][j]%=mod;int ret=1;for(int i=1; i<n; i++){for(int j=i+1; j<n; j++)while(a[j][i]){int t=a[i][i]/a[j][i];for(int k=i; k<n; k++)a[i][k]=(a[i][k]-a[j][k]*t)%mod;for(int k=i; k<n; k++)swap(a[i][k],a[j][k]);ret=-ret;}if(a[i][i]==0)return 0;ret=ret*a[i][i]%mod;}return (ret+mod)%mod;
}void Solve()
{sort(edge,edge+m);//按权值排序for(int i=1; i<=n; i++)//初始化并查集{f[i]=i;vist[i]=0;}LL Edge=-1;//记录相同的权值的边LL ans=1;for(int k=0; k<=m; k++){if(edge[k].c!=Edge||k==m)//一组相等的边,即权值都为Edge的边加完{for(int i=1; i<=n; i++){if(vist[i]){LL u=Find(i,U);V[u].push_back(i);vist[i]=0;}}for(int i=1; i<=n; i++) //枚举每个连通分量{if(V[i].size()>1){for(int a=1; a<=n; a++)for(int b=1; b<=n; b++)C[a][b]=0;int len=V[i].size();for(int a=0; a<len; a++) //构建Kirchhoff矩阵Cfor(int b=a+1; b<len; b++){int a1=V[i][a];int b1=V[i][b];C[a][b]=(C[b][a]-=G[a1][b1]);C[a][a]+=G[a1][b1];//连通分量的度C[b][b]+=G[a1][b1];}LL ret=(LL)det(C,len);ans=(ans*ret)%mod;//对V中的每一个连通块求生成树个数再相乘for(int a=0; a<len; a++)f[V[i][a]]=i;}}for(int i=1; i<=n; i++){U[i]=f[i]=Find(i,f);V[i].clear();}if(k==m)break;Edge=edge[k].c;}int a=edge[k].a;int b=edge[k].b;int a1=Find(a,f);int b1=Find(b,f);if(a1==b1)continue;vist[a1]=vist[b1]=1;U[Find(a1,U)]=Find(b1,U);//并查集操作G[a1][b1]++;G[b1][a1]++;}int flag=0;for(int i=2; i<=n&&!flag; i++)if(U[i]!=U[i-1])flag=1;if(m==0)flag=1;printf("%I64d\n",flag?0:ans%mod);}int main()
{while(scanf("%d%d%d",&n,&m,&mod),n+m+mod){memset(G,0,sizeof(G));for(int i=1; i<=n; i++)V[i].clear();for(int i=0; i<m; i++)scanf("%d%d%d",&edge[i].a,&edge[i].b,&edge[i].c);Solve();}return 0;
}
最小生成树计数模板及原理相关推荐
- 最小生成树计数(洛谷-P4208)
题目描述 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的).由于不同的 ...
- HDU 4408 - Minimum Spanning Tree(最小生成树计数)
有边权的最小生成树计数 模板 #pragma comment(linker, "/STACK:1024000000,1024000000") #include <iostre ...
- bzoj1016 [JSOI2008]最小生成树计数
1016: [JSOI2008]最小生成树计数 Time Limit: 1 Sec Memory Limit: 162 MB Submit: 6032 Solved: 2452 [Submit][ ...
- HDU 4408 Minimum Spanning Tree 最小生成树计数
http://acm.hdu.edu.cn/showproblem.php?pid=4408 题意:求最小生成树个数 题解:对于Kruskal算法,我们发现,最小生成树要想用多种方法就要有长度相同的边 ...
- HDU 4408 最小生成树计数详细解释
一些blog看我的好迷,假解释看哭我了,这是我自己的理解,一道题看1天.菜哭 HDU 4408 无向图的最小生成树计数原理 就是在kruskal处理边的时候不断地找到联通块(由多个同长度的边组成的联通 ...
- ecshop模板的原理分析
模板的原理 类似Smarty/ECShop这类模板的原理如下图所示. 1.首先是编译模板ECShop/Smart是利用PHP引擎,所以编译的结果是一个PHP文件,其编译过程就是 将分隔符{}替换成PH ...
- smarty模板引擎原理解析
//php 控制器文件 <?php //引入模板引擎文件 include("20130304.php"); $smarty = new TinySmarty(); $qq_n ...
- php模板引擎哪个好,php模板引擎原理是什么?
php模板引擎原理是作为视图层和模型层分离的一种有效解决方案,让前后端更好的分工协作,来自于经典的MVC模型,即[模型层-视图层-控制器模型],将M和V实现代码分离,从而使同一个程序可以使用不同的表现 ...
- 写一个迷你版Smarty模板引擎,对认识模板引擎原理非常好(附代码)
前些时间在看创智博客韩顺平的Smarty模板引擎教程,再结合自己跟李炎恢第二季开发中CMS系统写的tpl模板引擎.今天就写一个迷你版的Smarty引擎,虽然说我并没有深入分析过Smarty的源码,但是 ...
最新文章
- 结合脑成像技术与人工智能,破除自杀的“诅咒”
- 大数据教程(13.6)sqoop使用教程
- linux gcc-9.2.0 源码编译
- 尽快卸载这两款恶意浏览器插件!已有近50万用户安装
- Linux中如何将文件dump成16进制值
- IOS-关闭(退)键盘事件--转
- nginx index.php 端口,nginx-如果index.php不在nginx文件夹中,则禁止使用php fpm
- 从 Android 到 Java:如何从不同视角解决问题?
- 小自考计算机专业代码,自学考试有关专业分类及其代码
- php中获取系统信息的方法
- ajax将数据显示在class为content的标签中_利用selenium实现自动翻页爬取某鱼数据
- 程序员简历的10不要与7要
- C++持有Object-C对象时容易内存泄露
- 百万人学AI 万人在线大会, 15+ 场直播抢先看!
- MariaDB修改端口号
- linux w3m命令
- ERROR:STATUS_BROM_CMD_SEND_DA_FALL(0xC0060003)
- 电脑键盘快捷键使用大全
- 【coq】函数语言设计 笔记 03 - list
- [AV1] AV1 帧内预测