题意

给定一个 \(n\) 个点 \(m\) 条边的图,求最小生成树的个数。

\(\texttt{Data Range:}1\leq n\leq 100,1\leq m\leq 10^4\)

题解

一道好题。

根据本题后面提供的与那题正解没什么关联的方法可知,这个操作过程是这样的:

首先求出原图的某一个最小生成树,接下来考虑从小到大枚举最小生成树上边的边权 \(w\)。

将最小生成树上边权不为 \(w\) 的边保留下来进行缩点,接下来再连上不在最小生成树中边权为 \(w\) 的边。

这个时候会建出一个无向图,对每一个可能的 \(w\) 建出的图求一下生成树的个数乘起来即可。

证明的话可以利用 Kruskal 的性质,求生成树的时候使用 Matrix-Tree 定理即可。

模合数求行列式的方法是辗转相消,每一次需要交换两行,行列式要乘上 \(-1\),实在不理解可以看我代码

容易看出这个东西的复杂度是 \(O(n^3\log n)\) 的。

代码

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef int ll;
  4. typedef long long int li;
  5. const ll MAXN=251,MOD=31011;
  6. struct EdgeForKruskal{
  7. ll from,to,dist;
  8. inline bool operator <(const EdgeForKruskal &rhs)const
  9. {
  10. return this->dist<rhs.dist;
  11. }
  12. };
  13. EdgeForKruskal ed[2051],tree[MAXN];
  14. ll n,m,x,y,z,kk,res=1,totw;
  15. ll ffa[MAXN],bel[MAXN],mat[MAXN][MAXN],wt[MAXN];
  16. inline ll read()
  17. {
  18. register ll num=0,neg=1;
  19. register char ch=getchar();
  20. while(!isdigit(ch)&&ch!='-')
  21. {
  22. ch=getchar();
  23. }
  24. if(ch=='-')
  25. {
  26. neg=-1;
  27. ch=getchar();
  28. }
  29. while(isdigit(ch))
  30. {
  31. num=(num<<3)+(num<<1)+(ch-'0');
  32. ch=getchar();
  33. }
  34. return num*neg;
  35. }
  36. inline void add(ll x,ll y)
  37. {
  38. mat[x][y]--,mat[y][x]--,mat[x][x]++,mat[y][y]++;
  39. }
  40. inline ll find(ll x)
  41. {
  42. return x==ffa[x]?x:ffa[x]=find(ffa[x]);
  43. }
  44. inline void setup(ll n)
  45. {
  46. for(register int i=1;i<=n;i++)
  47. {
  48. ffa[i]=i;
  49. }
  50. }
  51. inline void merge(ll x,ll y)
  52. {
  53. ll fx=find(x),fy=find(y);
  54. fx!=fy?ffa[fy]=fx:1;
  55. }
  56. inline ll Kruskal()
  57. {
  58. ll tott=0;
  59. for(register int i=1;i<=m;i++)
  60. {
  61. if(find(ed[i].from)!=find(ed[i].to))
  62. {
  63. merge(ed[i].from,ed[i].to),tree[++tott]=ed[i];
  64. if(wt[totw]!=ed[i].dist)
  65. {
  66. wt[++totw]=ed[i].dist;
  67. }
  68. if(tott==n-1)
  69. {
  70. break;
  71. }
  72. }
  73. }
  74. return tott==n-1;
  75. }
  76. inline void mergePoint(ll wt)
  77. {
  78. for(register int i=1;i<n;i++)
  79. {
  80. tree[i].dist!=wt?merge(tree[i].from,tree[i].to):(void)1;
  81. }
  82. }
  83. inline ll det(ll n)
  84. {
  85. ll res=1,sgn=1,cof;
  86. for(register int i=1;i<=n;i++)
  87. {
  88. for(register int j=1;j<=n;j++)
  89. {
  90. mat[i][j]=(mat[i][j]+MOD)%MOD;
  91. }
  92. }
  93. for(register int i=1;i<=n;i++)
  94. {
  95. for(register int j=i+1;j<=n;j++)
  96. {
  97. while(mat[j][i])
  98. {
  99. cof=mat[i][i]/mat[j][i];
  100. for(register int k=i;k<=n;k++)
  101. {
  102. mat[i][k]=(mat[i][k]-(li)cof*mat[j][k]%MOD+MOD)%MOD;
  103. }
  104. swap(mat[i],mat[j]),sgn*=-1;
  105. }
  106. }
  107. }
  108. for(register int i=1;i<=n;i++)
  109. {
  110. res=(li)res*mat[i][i]%MOD;
  111. }
  112. return sgn==1?res:MOD-res;
  113. }
  114. inline ll calc(ll wt)
  115. {
  116. ll blk=0;
  117. memset(mat,0,sizeof(mat)),setup(n),mergePoint(wt);
  118. for(register int i=1;i<=n;i++)
  119. {
  120. find(i)==i?bel[i]=++blk:1;
  121. }
  122. for(register int i=1;i<=n;i++)
  123. {
  124. bel[i]=bel[find(i)];
  125. }
  126. for(register int i=1;i<=m;i++)
  127. {
  128. ed[i].dist==wt?add(bel[ed[i].from],bel[ed[i].to]):(void)1;
  129. }
  130. return det(blk-1);
  131. }
  132. int main()
  133. {
  134. n=read(),m=read();
  135. for(register int i=1;i<=m;i++)
  136. {
  137. x=read(),y=read(),z=read(),ed[i]=(EdgeForKruskal){x,y,z};
  138. }
  139. sort(ed+1,ed+m),setup(n);
  140. if(!Kruskal())
  141. {
  142. return puts("0"),0;
  143. }
  144. for(register int i=1;i<=totw;i++)
  145. {
  146. res=res*calc(wt[i])%MOD;
  147. }
  148. printf("%d\n",res);
  149. }

Luogu P4208 [JSOI2008]最小生成树计数的更多相关文章

  1. P4208 [JSOI2008]最小生成树计数

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

  2. [洛谷P4208][JSOI2008]最小生成树计数

    题目大意:有$n$个点和$m$条边(最多有$10$条边边权相同),求最小生成树个数 题解:对于所有最小生成树,每种边权的边数是一样的.于是就可以求出每种边权在最小生成树中的个数,枚举这种边的边集,求出 ...

  3. 洛谷P4208 [JSOI2008]最小生成树计数——题解

    题目传送 前置知识:对于同一个图的所有最小生成树,权值相等的边的数量相同. 可以简单证明一下: 我们可以从kruskal的过程考虑.这个算法把所有边按权值大小从小到大排序,然后按顺序看每条边,只要加上 ...

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

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

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

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

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

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

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

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

  8. 【bzoj1016】[JSOI2008]最小生成树计数

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

  9. bzoj1016: [JSOI2008]最小生成树计数(kruskal+dfs)

    1016: [JSOI2008]最小生成树计数 题目:传送门 题解: 神题神题%%% 据说最小生成树有两个神奇的定理: 1.权值相等的边在不同方案数中边数相等  就是说如果一种方案中权值为1的边有n条 ...

随机推荐

  1. python os模块方法详解

    os.access() 方法使用当前的uid/gid尝试访问路径.大部分操作使用有效的 uid/gid, 因此运行环境可以在 suid/sgid 环境尝试. 实例: os.chdir() 方法用于改变 ...

  2. 基础篇:深入解析JAVA反射机制

    目录 反射的概念 获取Class的三种方法 JAVA反射API 反射机制应用的场景 反射和JDK动态代理 欢迎指正文中错误 关注公众号,一起交流 参考文章 反射的概念 java的放射机制:在程序运行时 ...

  3. JUC并发编程--AQS

    转自: https://www.jianshu.com/p/d8eeb31bee5c 前言 在java.util.concurrent.locks包中有很多Lock的实现类,常用的有Reentrant ...

  4. JAVA并发笔记

    重入锁的特性, 避免死锁, 如果有锁的话, 不用重新加锁, 直接增加锁的次数.. Synchronize, ReentrantLock都是重入锁. 读写锁, ReentrantReadWriteLoc ...

  5. mapreduce的一些简单使用

    一.键值对RDD的创建 1.从文件中加载 /opt目录下创建wordky.txt文件. wordky.txt文件中输入以下三行字符: Hadoop is good Spark is fast Spar ...

  6. Copy As HTML From VSCode

    JS生成可自定义语法高亮HTMLcode cnblogs @ Orcim  !deprecated! 这里有更好的方案,具体看我的这篇博客博客代码高亮的另一种思路 这篇文章介绍了如何在博客里插入一段 ...

  7. python软件安装-Windows

     开发语言: 高级语言:Java.C.PHP.Go.ruby.c++   #字节码 低级语言:C.汇编                                           #机器码 语 ...

  8. 如何实现文章AI伪原创?

    language-ai 文章AI伪原创,文章自动生成,NLP,自然语言技术处理,DNN语言模型,词义相似度分析.全网首个AI伪原创开源应用类项目. 点击右侧about内的链接极速体验! 代码托管在gi ...

  9. react中 受控组件和 非受控组件 浅析

    一 受控组件 顾名思义,受控 也就是能够被控制,简而言之也就是 该组件ui的显示或者内部state逻辑的变化依赖外部的 props的传入. 二 非受控组件 顾名思义,非受控,也就是内部的视图变化,st ...

  10. 最近集训的图论(思路+实现)题目汇总(内容包含tarjan、分层图、拓扑、差分、奇怪的最短路):

    (集训模拟赛2)抢掠计划(tarjan强) 题目:给你n个点,m条边的图,每个点有点权,有一些点是"酒吧"点,终点只能在"酒吧",起点给定,路可以重复经过,但点 ...