参考于: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;
}

最小生成树计数模板及原理相关推荐

  1. 最小生成树计数(洛谷-P4208)

    题目描述 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的).由于不同的 ...

  2. HDU 4408 - Minimum Spanning Tree(最小生成树计数)

    有边权的最小生成树计数 模板 #pragma comment(linker, "/STACK:1024000000,1024000000") #include <iostre ...

  3. bzoj1016 [JSOI2008]最小生成树计数

    1016: [JSOI2008]最小生成树计数 Time Limit: 1 Sec  Memory Limit: 162 MB Submit: 6032  Solved: 2452 [Submit][ ...

  4. HDU 4408 Minimum Spanning Tree 最小生成树计数

    http://acm.hdu.edu.cn/showproblem.php?pid=4408 题意:求最小生成树个数 题解:对于Kruskal算法,我们发现,最小生成树要想用多种方法就要有长度相同的边 ...

  5. HDU 4408 最小生成树计数详细解释

    一些blog看我的好迷,假解释看哭我了,这是我自己的理解,一道题看1天.菜哭 HDU 4408 无向图的最小生成树计数原理 就是在kruskal处理边的时候不断地找到联通块(由多个同长度的边组成的联通 ...

  6. ecshop模板的原理分析

    模板的原理 类似Smarty/ECShop这类模板的原理如下图所示. 1.首先是编译模板ECShop/Smart是利用PHP引擎,所以编译的结果是一个PHP文件,其编译过程就是 将分隔符{}替换成PH ...

  7. smarty模板引擎原理解析

    //php 控制器文件 <?php //引入模板引擎文件 include("20130304.php"); $smarty = new TinySmarty(); $qq_n ...

  8. php模板引擎哪个好,php模板引擎原理是什么?

    php模板引擎原理是作为视图层和模型层分离的一种有效解决方案,让前后端更好的分工协作,来自于经典的MVC模型,即[模型层-视图层-控制器模型],将M和V实现代码分离,从而使同一个程序可以使用不同的表现 ...

  9. 写一个迷你版Smarty模板引擎,对认识模板引擎原理非常好(附代码)

    前些时间在看创智博客韩顺平的Smarty模板引擎教程,再结合自己跟李炎恢第二季开发中CMS系统写的tpl模板引擎.今天就写一个迷你版的Smarty引擎,虽然说我并没有深入分析过Smarty的源码,但是 ...

最新文章

  1. 结合脑成像技术与人工智能,破除自杀的“诅咒”
  2. 大数据教程(13.6)sqoop使用教程
  3. linux gcc-9.2.0 源码编译
  4. 尽快卸载这两款恶意浏览器插件!已有近50万用户安装
  5. Linux中如何将文件dump成16进制值
  6. IOS-关闭(退)键盘事件--转
  7. nginx index.php 端口,nginx-如果index.php不在nginx文件夹中,则禁止使用php fpm
  8. 从 Android 到 Java:如何从不同视角解决问题?
  9. 小自考计算机专业代码,自学考试有关专业分类及其代码
  10. php中获取系统信息的方法
  11. ajax将数据显示在class为content的标签中_利用selenium实现自动翻页爬取某鱼数据
  12. 程序员简历的10不要与7要
  13. C++持有Object-C对象时容易内存泄露
  14. 百万人学AI 万人在线大会, 15+ 场直播抢先看!
  15. MariaDB修改端口号
  16. linux w3m命令
  17. ERROR:STATUS_BROM_CMD_SEND_DA_FALL(0xC0060003)
  18. 电脑键盘快捷键使用大全
  19. 【coq】函数语言设计 笔记 03 - list
  20. [AV1] AV1 帧内预测

热门文章

  1. SR-TE Policy(思科)----explicit path 实验
  2. 控制Boos缓慢的转向主角
  3. CloudCompare打开pcd文件报错
  4. Linux gdisk与fdisk的使用方法
  5. 腾创秒会达视频会议产品简介
  6. WPF聚光灯光源学习
  7. 2021势如破竹 | 慧博科技连续5年荣获京东“京卓越”优秀服务商奖
  8. 在URL中添加UTM链接标签追踪流量效果
  9. iOS iBeacon 使用
  10. Kotlin 集合函数锦集,2021程序员进阶宝典