题目链接

最小生成树有两个性质:

1.在不同的MST中某种权值的边出现的次数是一定的。

2.在不同的MST中,连接完某种权值的边后,形成的连通块的状态是一样的。

\(Solution1\)

由这两个性质,可以先求一个MST,再枚举每一组边(权值相同的看做一组边),对每组边DFS(\(O(2^{10})\)),若某种方案连通性同MST相同(记录连通块个数即可)。则sum++。

最后根据乘法原理,最后的答案即为所有sum相乘。

\(Solution2\)

容易想到MatrixTree定理。

按边权从小到大处理每一组边,在加入这组边之前,之前的边会构成一些连通块,而这组边会一定会将某些连通块连在一起,如下图(我也不知道这图到底是哪的了):



把之前形成的每个连通块看做一个点,这样又成了一个生成树计数,生成树个数即为该种权值的边的方案数。如下图:



根据乘法原理,我们只要计算出每组边的这个方案,再乘起来就行了。

  1. //920kb 68ms
  2. #include <cstdio>
  3. #include <cctype>
  4. #include <vector>
  5. #include <cstring>
  6. #include <algorithm>
  7. #define gc() getchar()
  8. #define mod (31011)
  9. const int N=102,M=1002;
  10. int n,m,A[N][N],tmp[N][N],fa[N],bel[N],Ans;
  11. bool vis[N];
  12. std::vector<int> v[N];
  13. struct Edge{
  14. int fr,to,val;
  15. bool operator <(const Edge &a)const{
  16. return val<a.val;
  17. }
  18. }e[M];
  19. inline int read()
  20. {
  21. int now=0;register char c=gc();
  22. for(;!isdigit(c);c=gc());
  23. for(;isdigit(c);now=now*10+c-'0',c=gc());
  24. return now;
  25. }
  26. int Get_fa(int x,int *f){//两个并查集,一个维护MST中的连通性,一个维护所属连通块。
  27. return x==f[x]?x:f[x]=Get_fa(f[x],f);
  28. }
  29. void Gauss(int n)
  30. {
  31. for(int i=1; i<n; ++i)
  32. for(int j=1; j<n; ++j) (A[i][j]+=mod)%=mod;//!
  33. bool f=0;
  34. for(int j=1; j<n; ++j)
  35. {
  36. for(int i=j+1; i<n; ++i)
  37. while(A[i][j])
  38. {
  39. int t=A[j][j]/A[i][j];
  40. for(int k=j; k<n; ++k) A[j][k]=(A[j][k]-t*A[i][k]%mod+mod)%mod;
  41. for(int k=j; k<n; ++k) std::swap(A[i][k],A[j][k]);
  42. f^=1;
  43. }
  44. if(!A[j][j]) {Ans=0; break;}
  45. Ans=Ans*A[j][j]%mod;
  46. }
  47. if(f) Ans=mod-Ans;//!
  48. }
  49. void Calc()
  50. {
  51. for(int i=1; i<=n; ++i)
  52. if(vis[i]) v[Get_fa(i,bel)].push_back(i),vis[i]=0;//处理出每个连通块所含的点(原先连通块的代表元素)。
  53. for(int x=1; x<=n; ++x)
  54. if(v[x].size()>1)
  55. {
  56. memset(A,0,sizeof A);
  57. for(int i=0,lim=v[x].size(); i<lim; ++i)
  58. for(int a=v[x][i],b,j=i+1; j<lim; ++j)
  59. {
  60. b=v[x][j];
  61. if(tmp[a][b]){//tmp[][]作为边矩阵可以不清空,因为这俩连通块不会再同时出现了。
  62. A[i][j]=A[j][i]=-tmp[a][b];
  63. A[i][i]+=tmp[a][b], A[j][j]+=tmp[a][b];
  64. }
  65. }
  66. Gauss(v[x].size());
  67. }
  68. for(int i=1; i<=n; ++i)
  69. v[i].clear(), bel[i]=fa[i]=Get_fa(i,bel);//计算完某种边后把同一连通块的缩起来。
  70. }
  71. int main()
  72. {
  73. n=read(),m=read();
  74. for(int i=1; i<=m; ++i) e[i].fr=read(),e[i].to=read(),e[i].val=read();
  75. std::sort(e+1,e+1+m);
  76. for(int i=1; i<=n; ++i) fa[i]=bel[i]=i;
  77. e[0].val=e[1].val, Ans=1;
  78. for(int r1,r2,i=1; i<=m; ++i)
  79. {
  80. if(e[i].val!=e[i-1].val) Calc();
  81. r1=Get_fa(e[i].fr,fa), r2=Get_fa(e[i].to,fa);
  82. if(r1==r2) continue;
  83. // fa[r1]=r2;//暂时先不连接。
  84. vis[r1]=vis[r2]=1;
  85. ++tmp[r1][r2], ++tmp[r2][r1];//, ++tmp[r1][r1], ++tmp[r2][r2];//点的度数矩阵可以之后根据边处理,tmp[][]用来做边矩阵。最好这样,可以不清空。
  86. bel[Get_fa(e[i].fr,bel)]=Get_fa(e[i].to,bel);//统计出每个连通块。
  87. }
  88. Calc();//the last edge
  89. for(int i=1; i<n; ++i)
  90. if(bel[i]!=bel[i+1]) {Ans=0; break;}
  91. printf("%d",Ans);
  92. return 0;
  93. }

BZOJ.1016.[JSOI2008]最小生成树计数(Matrix Tree定理 Kruskal)的更多相关文章

  1. bzoj 1016 [JSOI2008]最小生成树计数——matrix tree(相同权值的边为阶段缩点)(码力)

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1016 就是缩点,每次相同权值的边构成的联通块求一下matrix tree.注意gauss里的 ...

  2. BZOJ 1016: [JSOI2008]最小生成树计数( kruskal + dfs )

    不同最小生成树中权值相同的边数量是一定的, 而且他们对连通性的贡献是一样的.对权值相同的边放在一起(至多10), 暴搜他们有多少种方案, 然后乘法原理. ----------------------- ...

  3. [BZOJ 1016] [JSOI2008] 最小生成树计数 【DFS】

    题目链接:BZOJ - 1016 题目分析 最小生成树的两个性质: 同一个图的最小生成树,满足: 1)同一种权值的边的个数相等 2)用Kruscal按照从小到大,处理完某一种权值的所有边后,图的连通性 ...

  4. [BZOJ]1016 JSOI2008 最小生成树计数

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

  5. BZOJ 1016 [JSOI2008]最小生成树计数 ——Matrix-Tree定理

    考虑从小往大加边,然后把所有联通块的生成树个数计算出来. 然后把他们缩成一个点,继续添加下一组. 最后乘法原理即可. 写起来很恶心 #include <queue> #include &l ...

  6. bzoj 1016: [JSOI2008]最小生成树计数【dfs+克鲁斯卡尔】

    有一个性质就是组成最小生成树总边权值的若干边权总是相等的 这意味着按边权排序后在权值相同的一段区间内的边能被选入最小生成树的条数是固定的 所以先随便求一个最小生成树,把每段的入选边数记录下来 然后对于 ...

  7. 【BZOJ 1016】 1016: [JSOI2008]最小生成树计数 (DFS|矩阵树定理)

    1016: [JSOI2008]最小生成树计数 Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树 ...

  8. 1016: [JSOI2008]最小生成树计数

    1016: [JSOI2008]最小生成树计数 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 6200  Solved: 2518[Submit][St ...

  9. @总结 - 7@ 生成树计数 —— matrix - tree 定理(矩阵树定理)与 prüfer 序列

    目录 @0 - 参考资料@ @0.5 - 你所需要了解的线性代数知识@ @1 - 矩阵树定理主体@ @证明 part - 1@ @证明 part - 2@ @证明 part - 3@ @证明 part ...

随机推荐

  1. Ubuntu16.04+CUDA8.0+cudnn6

    按之前的方法给TITAN X安装cuda8.0会发生循环登录的问题,因此换了一种安装方法 参考:https://www.jianshu.com/p/002ece426793,http://blog.c ...

  2. spring aop的五种通知类型

    昨天在腾讯课堂看springboot的视频,老师随口提问,尼玛竟然回答错了.特此记录! 问题: Spring web项目如果程序启动时出现异常,调用的是aop中哪类通知? 正确答案是: 异常返回通知. ...

  3. 【转载】linux下升级npm以及node

    原文:http://blog.csdn.net/qq_16339527/article/details/73008708 npm升级 废话不多说,直接讲步骤.先从容易的开始,升级npm. npm这款包 ...

  4. Python 正则表达式模块 (re) 简介

    Python 的 re 模块(Regular Expression 正则表达式)提供各种正则表达式的匹配操作,和 Perl 脚本的正则表达式功能类似,使用这一内嵌于 Python 的语言工具,尽管不能 ...

  5. tcpdump使用示例

    前言 这段时间一直在研究kubernetes当中的网络, 包括通过keepalived来实现VIP的高可用时常常不得不排查一些网络方面的问题, 在这里顺道梳理一下tcpdump的使用姿势, 若有写的不 ...

  6. SPLAY,LCT学习笔记(三)

    前两篇讲述了SPLAY模板操作,这一篇稍微介绍一下SPLAY的实际应用 (其实只有一道题,因为本蒟蒻就写了这一个) 例:bzoj 1014火星人prefix 由于本蒟蒻不会后缀数组,所以题目中给的提示 ...

  7. TCP连接的3次握手和4次挥手

    TCP连接的3次握手和4次挥手笔记 三次握手 TCP(Transmission Control Protocol) 传输控制协议 TCP是主机对主机层的传输控制协议,提供可靠的连接服务,采用三次握手确 ...

  8. Linux权限命令

    Linux 基础——权限管理命令chmod   一.Linux中的文件权限与目录权限 Linux中定义了3种访问权限,分别是r.w.x.其中r表示对象是可读的,w表示对象是可写的,x表示对象是可执行的 ...

  9. boto3--通过Python的SDK连接aws

    通过Python的SDK连接aws 参考: https://aws.amazon.com/cn/developers/getting-started/python/ aws上生成访问密钥 ID 和私有 ...

  10. JAVA开发工程师面试(1)

    我已经有很长一段时间没有更新博客了,难道是博主我变懒惰了吗?哎,这样可不行啊,我还有好多知识要学习,要和大家分享.以后我需要更加努力,改掉自己的惰性.本人文采不怎么样,只能是把自己所想的说出来,想和大 ...