思路:模拟kruskal的过程,可以发现对于所有权值相同的边,有很多种选择的方案,而且权值不同的边并不会相互影响,因为先考虑权值较小的边,权值比当前权值大的边显然不在考虑范围之内,而权值比当前权值小的边所组成的连通块已经经过缩点变成一个点了,因此处理权值相同的所有边可以看成是一个阶段,最后的答案也就是所有阶段的答案的乘积(乘法原理)。

那么如何来处理权值相同的方案数呢,同样还是考虑kruskal的过程,因为权值相同的边可能会组成很多个连通块,且连通块之间互不影响,因此只考虑单个连通块即可(还是乘法原理),如果一条边所连接的两个点不在一个连通块内,那么就把这条边算进答案,那么对于一个连通块kruskal的过程显然要让所有点连通,且所选的边恰好构成了一棵树,那么这样问题就转化成了如何求生成树的数量,利用matrix-tree定理,用高斯消元求解kirchhoff矩阵即可。

还有最后不要忘了判图中没有最小生成树的情况(图不连通)

  1. #include<iostream>
  2. #include<cstdio>
  3. #include<cstring>
  4. #include<algorithm>
  5. #include<vector>
  6. using namespace std;
  7. #define maxm 1005
  8. #define maxn 105
  9. #define mod 31011
  10.  
  11. int n,m,cnt,ans=1;
  12. int fa[maxn],pos[maxn];
  13. int K[maxn][maxn];
  14. bool vis[maxn];
  15.  
  16. vector<int> v[maxn];
  17.  
  18. struct edge{
  19. int from,to,val;
  20. bool operator <(const edge &a)const{return val<a.val;}
  21. }e[maxm];
  22.  
  23. inline int read(){
  24. int x=0;char ch=getchar();
  25. for (;ch<'0'||ch>'9';ch=getchar());
  26. for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
  27. return x;
  28. }
  29.  
  30. int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
  31.  
  32. int gauss(){
  33. int t,n=cnt-1,ans=1,f=1;
  34. for (int i=1;i<=n;i++)
  35. for (int j=1;j<=n;j++)
  36. K[i][j]%=mod;
  37. for (int i=1;i<n;i++){
  38. for (t=i;t<=n;t++) if (K[t][i]) break;if (t>n) return 0;
  39. if (t!=i){for (int j=1;j<=n;j++) swap(K[i][j],K[t][j]);f=-f;}
  40. for (int j=i+1;j<=n;j++)
  41. for (;K[j][i];){
  42. int t=K[i][i]/K[j][i];
  43. for (int k=i;k<=n;k++) K[i][k]=(K[i][k]-t*K[j][k])%mod;
  44. for (int k=i;k<=n;k++) swap(K[i][k],K[j][k]);
  45. f=-f;
  46. }
  47. }
  48. for (int i=1;i<=n;i++) ans=1ll*ans*K[i][i]%mod;
  49. return ans*f;
  50. }
  51.  
  52. void dfs(int x,int num){
  53. K[num][num]=v[x].size(),pos[x]=num;
  54. for (unsigned int i=0;i<v[x].size();i++){
  55. vis[v[x][i]]=0;
  56. if (!pos[v[x][i]]) pos[v[x][i]]=++cnt,K[num][pos[v[x][i]]]--,dfs(v[x][i],cnt);
  57. else K[num][pos[v[x][i]]]--;
  58. }
  59. }
  60.  
  61. int main(){
  62. n=read(),m=read();
  63. for (int i=1;i<=m;i++) e[i].from=read(),e[i].to=read(),e[i].val=read();
  64. for (int i=1;i<=n;i++) fa[i]=i;
  65. sort(e+1,e+m+1);
  66. for (int i=1;i<=m+1;i++){
  67. int ck=find(1);
  68. for (int j=2;j<=n;j++) if (find(j)!=ck){ck=0;break;}
  69. if (ck) break;
  70. int x=find(e[i].from),y=find(e[i].to);
  71. if (x!=y) vis[x]=1,vis[y]=1,v[x].push_back(y),v[y].push_back(x);
  72. if (e[i].val!=e[i+1].val){
  73. for (int j=1;j<=n;j++)
  74. if (vis[j]){
  75. for (int a=1;a<=cnt;a++)
  76. for (int b=1;b<=cnt;b++)
  77. K[a][b]=0;
  78. memset(pos,0,sizeof(pos));
  79. vis[j]=0,cnt=1,dfs(j,cnt);
  80. ans=1ll*ans*gauss()%mod;
  81. }
  82. for (int j=1;j<=n;j++){
  83. for (unsigned int k=0;k<v[j].size();k++){
  84. int x=find(j),y=find(v[j][k]);
  85. if (x!=y) fa[x]=y;
  86. }
  87. v[j].clear();
  88. }
  89. }
  90. }
  91. int check=find(1);
  92. for (int i=2;i<=n;i++) if (find(i)!=check){check=0;break;}
  93. printf("%d\n",check?ans:0);
  94. return 0;
  95. }

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

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

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

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

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

  3. BZOJ1016:[JSOI2008]最小生成树计数(最小生成树,DFS)

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

  4. [bzoj1016][JSOI2008]最小生成树计数 (Kruskal + Matrix Tree 定理)

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

  5. 【Matrix-tree定理】【并查集】【kruscal算法】bzoj1016 [JSOI2008]最小生成树计数

    题意:求一个图的最小生成树个数. 矩阵树定理:一张无向图的生成树个数 = (度数矩阵 - 邻接矩阵)的任意一个n-1主子式的值. 度数矩阵除了对角线上D[i][i]为i的度数(不计自环)外,其他位置是 ...

  6. [BZOJ1016][JSOI2008]最小生成树计数(结论题)

    题目:http://www.lydsy.com:808/JudgeOnline/problem.php?id=1016 分析: 首先有个性质:如果边集E.E'都可以表示一个图G的最小生成树(当然E和E ...

  7. [BZOJ1016] [JSOI2008] 最小生成树计数 (Kruskal)

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

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

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

  9. 2018.09.24 bzoj1016: [JSOI2008]最小生成树计数(并查集+搜索)

    传送门 正解是并查集+矩阵树定理. 但由于数据范围小搜索也可以过. 我们需要知道最小生成树的两个性质: 不同的最小生成树中,每种权值的边出现的个数是确定的 不同的生成树中,某一种权值的边连接完成后,形 ...

  10. [BZOJ1016][JSOI2008]最小生成树计数 最小生成树 搜索

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1016 做这道题之前需要知道一些结论,同一个图的最小生成树中相同权值的边的个数是不会变的,如 ...

随机推荐

  1. RocketMQ入门(3)拉取消息

    转自:http://www.changeself.net/archives/rocketmq入门(3)拉取消息.html RocketMQ入门(3)拉取消息 RocketMQ不止可以直接推送消息,在消 ...

  2. [Bootstrap] 2. class 'row' & 'col-md-x' & 'col-md-offset-x'

    Usually when desgin a web page, we think building the page in grid. Bootstrap can help us to do that ...

  3. [Python]linux自己定义Python脚本命令

    在window下写好的程序配置到Linux上,要实现随意文件夹下的命令调用. 因为初学Linux,这里从文件传输等最主要的方法入手,记录配置的过程中遇到的各种问题. 连接远端server 这里使用pu ...

  4. Android界面刷新

    Android的invalidate与postInvalidate都是用来刷新界面的,用法区别在于: 1)invalidate():实例化一个Handler对象,并重写handleMessage方法调 ...

  5. mysql命令行方式添加用户及设置权限

    以前总是喜欢通过phpmyadmin去添加用户和数据库,这次装完系统后,配置了一大堆东东,实在不想安装phpmyadmin了,就通过命令行方式创建了数据库和设置权限,记录一下,免得以后总是百度 关键步 ...

  6. TamperData火狐插件启用

    今天第一次在准备学习sqlmap的时候看到有人使用了这个TamperData火狐的插件,所以我也安装来看看,光是安装了,但是重启浏览器之后不知道怎么启用,得不到想要的运行界面,所以在这里记录一下,为自 ...

  7. [记录]使用Gitblit 在windows 上安装Git Server

    参考了: Windows平台下搭建Git服务器的图文教程 主要修改了:data/gitblit.properties # Include Gitblit's 'defaults.properties' ...

  8. otf字体转ttf字体

    可以使用Font creator进行转换字体. 绿色版下载链接 Font Creator(字体编辑软件下载)V9.0官方版 或者我的百度云:http://pan.baidu.com/s/1c1jjfm ...

  9. 针对SharePointFarm场时安装部署OWA的步骤

    下面文章列出针对SharePointFarm场时安装部署OWA的步骤: http://blogs.technet.com/b/meamcs/archive/2013/03/27/office-web- ...

  10. 【C语言】03-printf和scanf函数

    一.printf函数 这是在stdio.h中声明的一个函数,因此使用前必须加入#include <stdio.h>,使用它可以向标准输出设备(比如屏幕)输出数据 1.用法 1> pr ...