【Matrix-tree定理】【并查集】【kruscal算法】bzoj1016 [JSOI2008]最小生成树计数
题意:求一个图的最小生成树个数。
矩阵树定理:一张无向图的生成树个数 = (度数矩阵 - 邻接矩阵)的任意一个n-1主子式的值。
度数矩阵除了对角线上D[i][i]为i的度数(不计自环)外,其他位置是0。
邻接矩阵G[i][j]的值为i与j之间的边数(重边要记入)。
一个定理:一个图的所有MST中,相同权值的边数肯定是相等的。
于是将边从小到大排序之后,根据权值划分阶段,将之前的点全缩点,这一阶段的边中仅考虑当前权值的边,然后把图划分成多个连通块,对每个连通块使用矩阵树定理求生成树个数,该阶段的值就是所有连通块乘起来。然后最终答案就是所有阶段的值乘起来。
缩点使用了动态维护并查集的方法。
无法求出生成树的情况要特判。
md另外这题模的数是合数,所以高斯消元贼恶心……用了特殊的处理方法
#include<cstdio>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
typedef vector<int>::iterator ITER;
vector<int>bel[105];
int v[2005],first[105],next[2005],w[2005],e;
void AddEdge(int U,int V,int W){
v[++e]=V;
w[e]=W;
next[e]=first[U];
first[U]=e;
}
int fa[105];
int find(int x){
return x==fa[x] ? x : fa[x]=find(fa[x]);
}
const int MOD=31011;
struct Edge{
int u,v,w;
Edge(const int &u,const int &v,const int &w){
this->u=u;
this->v=v;
this->w=w;
}
Edge(){}
void read(){
scanf("%d%d%d",&u,&v,&w);
}
}es[1005];
bool cmp(const Edge &a,const Edge &b){
return a.w<b.w;
}
int n,m;
bool vis[105];
int num[105],p,now,A[105][105],ans=1;
void dfs(int U){
num[U]=++p;
vis[U]=1;
for(ITER it=bel[U].begin();it!=bel[U].end();++it){
for(int i=first[*it];i;i=next[i]){
int V;
if(w[i]==now && (V=find(v[i]))!=U){
// printf("%d %d\n",U,V);
if(!vis[V]){
dfs(V);
}
++A[num[V]][num[V]];
A[num[U]][num[V]]=(A[num[U]][num[V]]-1+MOD)%MOD;
}
}
}
}
int N;
int guass_jordan()
{
int res=1;
for(int i=1;i<=N;i++){
for (int j=i+1;j<=N;j++){
int a=A[i][i],b=A[j][i];
while(b)
{
int t=a/b;
a%=b;
swap(a,b);
for(int k=i;k<=N;k++){
A[i][k]=(A[i][k]-A[j][k]*t%MOD+MOD)%MOD;
}
for(int k=i;k<=N;k++){
swap(A[i][k],A[j][k]);
}
res=res*(MOD-1)%MOD;
}
}
if(!A[i][i]){
return 0;
}
res=(res*A[i][i])%MOD;
}
return res;
}
int tot;
int main(){
// freopen("bzoj1016.in","r",stdin);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
fa[i]=i;
bel[i].push_back(i);
}
for(int i=1;i<=m;++i){
es[i].read();
AddEdge(es[i].u,es[i].v,es[i].w);
AddEdge(es[i].v,es[i].u,es[i].w);
}
sort(es+1,es+m+1,cmp);
for(int i=1;i<=m;++i){
if(es[i].w!=es[i-1].w){
now=es[i].w;
memset(vis,0,sizeof(vis));
for(int j=1;j<=n;++j){
if(j==fa[j] && !vis[j]){
p=0;
memset(num,0,sizeof(num));
dfs(j);
N=p-1;
ans=ans*guass_jordan()%MOD;
for(int k=1;k<=p;++k){
for(int l=1;l<=p;++l){
A[k][l]=0;
}
}
}
}
}
int f1=find(es[i].u),f2=find(es[i].v);
if(f1!=f2){
fa[f1]=f2;
++tot;
for(ITER it=bel[f1].begin();it!=bel[f1].end();++it){
bel[f2].push_back(*it);
}
bel[f1].clear();
}
}
printf("%d\n",tot==n-1 ? ans : 0);
return 0;
}
【Matrix-tree定理】【并查集】【kruscal算法】bzoj1016 [JSOI2008]最小生成树计数的更多相关文章
- [bzoj1016][JSOI2008]最小生成树计数 (Kruskal + Matrix Tree 定理)
Description 现在给出了一个简单无向加权图.你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树.(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的 ...
- 2018.09.24 bzoj1016: [JSOI2008]最小生成树计数(并查集+搜索)
传送门 正解是并查集+矩阵树定理. 但由于数据范围小搜索也可以过. 我们需要知道最小生成树的两个性质: 不同的最小生成树中,每种权值的边出现的个数是确定的 不同的生成树中,某一种权值的边连接完成后,形 ...
- 【kruscal】【最小生成树】【搜索】bzoj1016 [JSOI2008]最小生成树计数
不用Matrix-tree定理什么的,一边kruscal一边 对权值相同的边 暴搜即可.将所有方案乘起来. #include<cstdio> #include<algorithm&g ...
- bzoj1016 [JSOI2008]最小生成树计数——Kruskal+矩阵树定理
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1016 从 Kruskal 算法的过程来考虑产生多种方案的原因,就是边权相同的边有一样的功能, ...
- 【证明与推广与背诵】Matrix Tree定理和一些推广
[背诵手记]Matrix Tree定理和一些推广 结论 对于一个无向图\(G=(V,E)\),暂时钦定他是简单图,定义以下矩阵: (入)度数矩阵\(D\),其中\(D_{ii}=deg_i\).其他= ...
- K:Union-Find(并查集)算法
相关介绍: 并查集的相关算法,是我见过的,最为之有趣的算法之一.并查集是一种树型的数据结构,用于处理一些不相交集合(Disjoint Sets)的合并及查询问题.其相关的实现代码较为简短,实现思想也 ...
- 【BZOJ2959】长跑(Link-Cut Tree,并查集)
[BZOJ2959]长跑(Link-Cut Tree,并查集) 题面 BZOJ 题解 如果保证不出现环的话 妥妥的\(LCT\)傻逼题 现在可能会出现环 环有什么影响? 那就可以沿着环把所有点全部走一 ...
- BZOJ.4031.[HEOI2015]小Z的房间(Matrix Tree定理 辗转相除)
题目链接 辗转相除解行列式的具体实现? 行列式的基本性质. //864kb 64ms //裸的Matrix Tree定理.练习一下用辗转相除解行列式.(因为模数不是质数,所以不能直接乘逆元来高斯消元. ...
- @总结 - 7@ 生成树计数 —— matrix - tree 定理(矩阵树定理)与 prüfer 序列
目录 @0 - 参考资料@ @0.5 - 你所需要了解的线性代数知识@ @1 - 矩阵树定理主体@ @证明 part - 1@ @证明 part - 2@ @证明 part - 3@ @证明 part ...
随机推荐
- Chinese Rings (九连环+矩阵快速幂)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2842 题目: Problem Description Dumbear likes to play th ...
- bzoj 3453 数论
首先我们知道对于f(x)来说,它是一个k次的多项式,那么f(x)的通项公式可以表示成一个k+1次的式子,且因为f(x)没有常数项,所以我们设这个式子为 f(x)=Σ(a[i]*x^i) (1<= ...
- AndroidStudio 添加Selector文件,在res文件夹下添加文件夹
在res文件夹下添加文件夹: 添加Selector文件:
- Eclipse连接海马模拟器
找到海马模拟器安装目录: 使用cmd 命令进入命令行:D: cd:D:\Program Files (x86)\Droid4X 进入模拟器所在目录 运行adb connect 127.0.0.1:26 ...
- Python代码这样写更优雅(转)
1.变量交换 大部分编程语言中交换两个变量的值时,不得不引入一个临时变量: >>> a = 1>>> b = 2>>> tmp = a>&g ...
- (转)LSI SAS 1068E Raid CentOS 5.5 安装实例浪潮NF5220系列 分类: linux
新来了一批服务器,全都是清一色的国产服务器,相同的阵列卡,令人头疼的是Linux标准内核不包含该raid驱动,需要单独安装,如果是新升级内核,肯定需要编译进去该raid驱动.一.先把主板自带的驱动光盘 ...
- python爬虫面试总结
1.爬虫有哪些模块? 答: URL管理模块:维护已经爬取的URL集合和未爬取的URL集合,并提供获取新URL链接的接口 HTML下载模块:从URL管理器中获取未爬取的URL链接并下载HTML网页 HT ...
- C#使用Linq To XML读取XML,Linq生成XML,Linq创建带属性或带节点XML
using System; using System.Linq; using System.Xml.Linq; namespace Sample2 { class Program { static v ...
- linux运维记
nmap 127.0.0.1 命令查看当前服务器对外有多少端口,用于检查漏洞 vim ctrl+z ,jobs,fg 切换控制应用程序 vim 执行命令 #!sh aa.sh执行命令 运维系统监控开源 ...
- [ python ] 类中的一些特殊方法
item系列 __getitem__(self, item) 对象通过 object[key] 触发 __setitem__(self, key, value) 对象通过 object[key] = ...