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

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

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

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
using namespace std;
#define maxm 1005
#define maxn 105
#define mod 31011 int n,m,cnt,ans=1;
int fa[maxn],pos[maxn];
int K[maxn][maxn];
bool vis[maxn]; vector<int> v[maxn]; struct edge{
int from,to,val;
bool operator <(const edge &a)const{return val<a.val;}
}e[maxm]; inline int read(){
int x=0;char ch=getchar();
for (;ch<'0'||ch>'9';ch=getchar());
for (;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x;
} int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} int gauss(){
int t,n=cnt-1,ans=1,f=1;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
K[i][j]%=mod;
for (int i=1;i<n;i++){
for (t=i;t<=n;t++) if (K[t][i]) break;if (t>n) return 0;
if (t!=i){for (int j=1;j<=n;j++) swap(K[i][j],K[t][j]);f=-f;}
for (int j=i+1;j<=n;j++)
for (;K[j][i];){
int t=K[i][i]/K[j][i];
for (int k=i;k<=n;k++) K[i][k]=(K[i][k]-t*K[j][k])%mod;
for (int k=i;k<=n;k++) swap(K[i][k],K[j][k]);
f=-f;
}
}
for (int i=1;i<=n;i++) ans=1ll*ans*K[i][i]%mod;
return ans*f;
} void dfs(int x,int num){
K[num][num]=v[x].size(),pos[x]=num;
for (unsigned int i=0;i<v[x].size();i++){
vis[v[x][i]]=0;
if (!pos[v[x][i]]) pos[v[x][i]]=++cnt,K[num][pos[v[x][i]]]--,dfs(v[x][i],cnt);
else K[num][pos[v[x][i]]]--;
}
} int main(){
n=read(),m=read();
for (int i=1;i<=m;i++) e[i].from=read(),e[i].to=read(),e[i].val=read();
for (int i=1;i<=n;i++) fa[i]=i;
sort(e+1,e+m+1);
for (int i=1;i<=m+1;i++){
int ck=find(1);
for (int j=2;j<=n;j++) if (find(j)!=ck){ck=0;break;}
if (ck) break;
int x=find(e[i].from),y=find(e[i].to);
if (x!=y) vis[x]=1,vis[y]=1,v[x].push_back(y),v[y].push_back(x);
if (e[i].val!=e[i+1].val){
for (int j=1;j<=n;j++)
if (vis[j]){
for (int a=1;a<=cnt;a++)
for (int b=1;b<=cnt;b++)
K[a][b]=0;
memset(pos,0,sizeof(pos));
vis[j]=0,cnt=1,dfs(j,cnt);
ans=1ll*ans*gauss()%mod;
}
for (int j=1;j<=n;j++){
for (unsigned int k=0;k<v[j].size();k++){
int x=find(j),y=find(v[j][k]);
if (x!=y) fa[x]=y;
}
v[j].clear();
}
}
}
int check=find(1);
for (int i=2;i<=n;i++) if (find(i)!=check){check=0;break;}
printf("%d\n",check?ans:0);
return 0;
}

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. 【C++深入浅出】设计模式学习之单例模式

    但凡成为大家公认的模式,都是有一些不可小觑的威力,今天分享一个简单的设计模式:单例模式. 单例模式用于一些只希望有一个实例的类或者只希望执行一次的操作:校长只能有一个.老板只能有一个.用户点击弹窗只希 ...

  2. Tomcat无法部署项目

    设置项目的Jdk,compire version 增加java EE 如果有必要,现在项目根目录下放置.mymetadata文件 <?xml version="1.0" en ...

  3. dotnetfx35.exe

    http://download.microsoft.com/download/2/0/e/20e90413-712f-438c-988e-fdaa79a8ac3d/dotnetfx35.exe

  4. Fatal error: Using $this when not in object context in 解决方法

    Fatal error: Using $this when not in object context in 解决方法 粗心造成的错误 $this 只存在于下面情况 $obj = new object ...

  5. [Webpack] Use the Webpack Dashboard to Monitor Webpack Operations

    Learn how to use the new Webpack Dashboard from Formidable Labs to display a pretty, useful output f ...

  6. CSS:表格样式(设置表格边框/文字/背景的样式)

    使用CSS能够制作出十分精美的表格. 代码整理自w3school:http://www.w3school.com.cn 效果图: 代码: <!DOCTYPE html PUBLIC " ...

  7. Gradle的安装与配置

    Gradle的下载地址:http://gradle.org/gradle-download/ Tips:要先安装JDK并配置其环境变量. 下载完成后将里面的文件解压出来,如下图所示: 添加一个名称为G ...

  8. php中body下出现莫名空白字符

    php中body下出现莫名空白字符,出现原因 1. 模板前面有莫名格式空格,删不掉,必须把模板里面东西复制出来,新建一个文件弄进去,覆盖原有的 2.docutype前面出现代码,也会导致此问题(有可能 ...

  9. 斐波那契数列_java版本

    package 斐波那契数列; public class fbnq { public static void main(String[] args){ System.out.println(fibon ...

  10. IIS 之 查看并发连接数

    如果要查看IIS连接数,最简单方便的方法是通过“网站统计”来查看,“网站统计”的当前在线人数可以认为是当前IIS连接数.然而,“网站统计”的当前在线人数统计时间较长,一般为10分钟或15分钟,再加上统 ...