题意

给定一个 \(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)\) 的。

代码

#include<bits/stdc++.h>
using namespace std;
typedef int ll;
typedef long long int li;
const ll MAXN=251,MOD=31011;
struct EdgeForKruskal{
ll from,to,dist;
inline bool operator <(const EdgeForKruskal &rhs)const
{
return this->dist<rhs.dist;
}
};
EdgeForKruskal ed[2051],tree[MAXN];
ll n,m,x,y,z,kk,res=1,totw;
ll ffa[MAXN],bel[MAXN],mat[MAXN][MAXN],wt[MAXN];
inline ll read()
{
register ll num=0,neg=1;
register char ch=getchar();
while(!isdigit(ch)&&ch!='-')
{
ch=getchar();
}
if(ch=='-')
{
neg=-1;
ch=getchar();
}
while(isdigit(ch))
{
num=(num<<3)+(num<<1)+(ch-'0');
ch=getchar();
}
return num*neg;
}
inline void add(ll x,ll y)
{
mat[x][y]--,mat[y][x]--,mat[x][x]++,mat[y][y]++;
}
inline ll find(ll x)
{
return x==ffa[x]?x:ffa[x]=find(ffa[x]);
}
inline void setup(ll n)
{
for(register int i=1;i<=n;i++)
{
ffa[i]=i;
}
}
inline void merge(ll x,ll y)
{
ll fx=find(x),fy=find(y);
fx!=fy?ffa[fy]=fx:1;
}
inline ll Kruskal()
{
ll tott=0;
for(register int i=1;i<=m;i++)
{
if(find(ed[i].from)!=find(ed[i].to))
{
merge(ed[i].from,ed[i].to),tree[++tott]=ed[i];
if(wt[totw]!=ed[i].dist)
{
wt[++totw]=ed[i].dist;
}
if(tott==n-1)
{
break;
}
}
}
return tott==n-1;
}
inline void mergePoint(ll wt)
{
for(register int i=1;i<n;i++)
{
tree[i].dist!=wt?merge(tree[i].from,tree[i].to):(void)1;
}
}
inline ll det(ll n)
{
ll res=1,sgn=1,cof;
for(register int i=1;i<=n;i++)
{
for(register int j=1;j<=n;j++)
{
mat[i][j]=(mat[i][j]+MOD)%MOD;
}
}
for(register int i=1;i<=n;i++)
{
for(register int j=i+1;j<=n;j++)
{
while(mat[j][i])
{
cof=mat[i][i]/mat[j][i];
for(register int k=i;k<=n;k++)
{
mat[i][k]=(mat[i][k]-(li)cof*mat[j][k]%MOD+MOD)%MOD;
}
swap(mat[i],mat[j]),sgn*=-1;
}
}
}
for(register int i=1;i<=n;i++)
{
res=(li)res*mat[i][i]%MOD;
}
return sgn==1?res:MOD-res;
}
inline ll calc(ll wt)
{
ll blk=0;
memset(mat,0,sizeof(mat)),setup(n),mergePoint(wt);
for(register int i=1;i<=n;i++)
{
find(i)==i?bel[i]=++blk:1;
}
for(register int i=1;i<=n;i++)
{
bel[i]=bel[find(i)];
}
for(register int i=1;i<=m;i++)
{
ed[i].dist==wt?add(bel[ed[i].from],bel[ed[i].to]):(void)1;
}
return det(blk-1);
}
int main()
{
n=read(),m=read();
for(register int i=1;i<=m;i++)
{
x=read(),y=read(),z=read(),ed[i]=(EdgeForKruskal){x,y,z};
}
sort(ed+1,ed+m),setup(n);
if(!Kruskal())
{
return puts("0"),0;
}
for(register int i=1;i<=totw;i++)
{
res=res*calc(wt[i])%MOD;
}
printf("%d\n",res);
}

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-数组+递归实现简单代数式运算

    #!/usr/bin/env python3# -*- coding: utf-8 -*-#思路: #代数式是为字符串 #先将字符串处理为数值与运算符号的数组 #逐项读入数组 #每一次处理不少过两个变 ...

  2. 021 01 Android 零基础入门 01 Java基础语法 03 Java运算符 01 赋值运算符

    021 01 Android 零基础入门 01 Java基础语法 03 Java运算符 01 赋值运算符 本文知识点:Java中的赋值运算符 赋值运算符 赋值运算符从右往左运算 赋值运算符左边不能是常 ...

  3. linux内核输入子系统分析

    1.为何引入input system? 以前我们写一些输入设备(键盘.鼠标等)的驱动都是采用字符设备.混杂设备处理的.问题由此而来,Linux开源社区的大神们看到了这大量输入设备如此分散不堪,有木有可 ...

  4. 利用HDFS实现ElasticSearch7.2容灾方案

    利用HDFS实现ElasticSearch7.2容灾方案 目录 利用HDFS实现ElasticSearch7.2容灾方案 前言 快照版本兼容 备份集群 HDFS文件系统 软件下载 JDK环境 配置系统 ...

  5. JProfiler 教程 使用说明

    JProfiler    (本文原创转载请注明) 简介 JProfiler是一个重量级的JVM监控工具,提供对JVM精确监控,其中堆遍历.CPU剖析.线程剖析看成定位当前系统瓶颈的得力工具.可以统计压 ...

  6. Linux_centOS_5.7_64下如何安装jdk1.8&mysql

    本文主要介绍的是如何是Linux环境下安装JDK的,因为Linux环境下,很多时候也离不开Java的,下面笔者就和大家一起分享如何jdk1.8的过程吧. JDK安装 环境 操作系统:Centos7_6 ...

  7. springCloud微服务调用失败【CannotGetJdbcConnectionException: Failed to obtain JDBC Connection】

    详情如下: 2019-07-28 10:56:18.229 ERROR 16212 --- [nio-8081-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet ...

  8. 手把手教你AspNetCore WebApi:缓存(MemoryCache和Redis)

    前言 这几天小明又有烦恼了,系统上线一段时间后,系统性能出现了问题,马老板很生气,叫小明一定要解决这个问题.性能问题一般用什么来解决呢?小明第一时间想到了缓存. 什么是缓存 缓存是实际工作中非常常用的 ...

  9. CSS的背景

    CSS的背景 1. 背景颜色background-color div { background-color: 颜色值; } 一般情况下元素背景颜色默认是transparent(透明). 2. 背景图片 ...

  10. 使用Python学习win32库进行内存读写

    前言: 上一周,在52的精华帖中,看到有位大佬用Python制作了鬼泣5的修改器,看完才知道,原来Python也可以对内存进行操作,出于对技术的好奇,看完以后,决定自己也尝试一下. 要用到的工具: C ...