qwq

这个题目真的是很好的一个题啊

qwq

其实一开始想这个题,肯定是无从下手。

首先,我们会发现,对于无向图的一个最小生成树来说,只有当存在一些边与内部的某些边权值相同的时候且能等效替代的时候,才会有多种最小生成树。

那我们不妨对于原图先随意求一个最小生成树,然后对于出现在最小生成树上的每个权值计算贡献。

我们每次删除所有该权值的边,然后把剩下的点能缩点的进行缩点(用并查集来维护)

然后,我们构造一个联通块的拉普拉斯矩阵。也就是说,加入所有的在图中的,权值为该值的边。然后我们只需要求能重新构成生成树的连接方式。

(这里重边要当成不同的边来算!!因为表示的方案并不相同)

那么我们考虑对于当前权值的边的一个合法的连接,是要求能将所有的联通块变成一个树。

换句话说,对于每一条边,他的合法连接方式数量,就是这个图的生成树个数。

假设每个权值的合法连接方式是\(f[i]\)

那么最终的$$ans=\prod_{i \in tree} f[i]$$

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>
#include<set>
#define mk make_pair
#define ll long long
#define int long long
#include<unordered_map>
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)) {if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 510;
const int mod = 31011;
const int maxm = 1e5+1e2;
struct Edge{
int u,v,w;
};
Edge e[maxm];
int tag[maxm];
int n,m;
int ans=1;
vector<int> v;
int fa[maxn];
int vis[maxm];
int a[maxn][maxn];
Edge now[maxm];
int sum;
bool cmp(Edge a,Edge b)
{
return a.w<b.w;
}
int find(int x)
{
if (fa[x]!=x) fa[x]=find(fa[x]);
return fa[x];
}
void kruskal()
{
sort(e+1,e+1+m,cmp);
for (int i=1;i<=n;i++) fa[i]=i;
for (int i=1;i<=m;i++)
{
int x=e[i].u;
int y=e[i].v;
int f1 = find(x);
int f2 = find(y);
if (f1==f2) continue;
tag[i]=1;
fa[f1]=fa[f2];
v.push_back(e[i].w);
++sum;
}
}
int gauss(int n)
{
int k=1;
int ans=1;
int ff=0;
for (int i=1;i<=n;i++)
{
int now =k;
while (now<=n && (!a[now][i])) now++;
if (now==n+1) continue;
if (now!=k) ff++;
for (int j=1;j<=n+1;j++) swap(a[now][i],a[k][i]);
for (int j=i+1;j<=n;j++)
{
while (a[j][i])
{
int t = a[k][i]/a[j][i];
for (int p=i;p<=n;p++) a[k][p]-=t*a[j][p];
swap(a[k],a[j]);
ff++;
}
}
ans=ans*a[i][i]%mod;
k++;
}
if(ff&1) ans=(mod-ans);
return ans;
}
int tt[maxn];
int ymh[maxn];
void count(int val)
{
memset(tt,0,sizeof(tt));
memset(a,0,sizeof(a));
memset(ymh,0,sizeof(ymh));
int num=0;
int top=0;
for (int i=1;i<=m;i++)
{
if (tag[i] && e[i].w!=val)
now[++top]=e[i],tt[e[i].u]++,tt[e[i].v]++;
else
if (e[i].w==val) tt[e[i].u]++,tt[e[i].v]++;
}
for (int i=1;i<=n;i++) fa[i]=i;
for (int i=1;i<=top;i++)
{
int x=now[i].u;
int y=now[i].v;
tt[x]++;
tt[y]++;
int f1 = find(x),f2=find(y);
if (x==y) continue;
fa[f1]=fa[f2];
}
for (int i=1;i<=n;i++)
{
if (!tt[i]) continue;
if (find(i)==i)
{
ymh[i]=++num;
}
}
for (int i=1;i<=m;i++)
{
if (e[i].w==val)
{
int x=ymh[find(e[i].u)];
int y=ymh[find(e[i].v)];
a[x][y]--;
a[y][x]--;
a[x][x]++;
a[y][y]++;
}
}
ans=ans*gauss(num-1)%mod;
}
signed main()
{
n=read(),m=read();
for (int i=1;i<=m;i++)
{
e[i].u=read();
e[i].v=read();
e[i].w=read();
}
kruskal();
if (sum!=n-1)
{
cout<<0;
return 0;
}
sort(v.begin(),v.end());
for (int i=0;i<v.size();i++)
{
if (i==0)count(v[i]);
else if (v[i]!=v[i-1]) count(v[i]);
}
cout<<ans;
return 0;
}

洛谷4208 JSOI2008最小生成树计数(矩阵树定理+高斯消元)的更多相关文章

  1. [spoj104][Highways] (生成树计数+矩阵树定理+高斯消元)

    In some countries building highways takes a lot of time... Maybe that's because there are many possi ...

  2. BZOJ4031 [HEOI2015]小Z的房间 【矩阵树定理 + 高斯消元】

    题目链接 BZOJ4031 题解 第一眼:这不裸的矩阵树定理么 第二眼:这个模\(10^9\)是什么鬼嘛QAQ 想尝试递归求行列式,发现这是\(O(n!)\)的.. 想上高斯消元,却又处理不了逆元这个 ...

  3. P3317 [SDOI2014]重建 变元矩阵树定理 高斯消元

    传送门:https://www.luogu.org/problemnew/show/P3317 这道题的推导公式还是比较好理解的,但是由于这个矩阵是小数的,要注意高斯消元方法的使用: #include ...

  4. luoguP4208 [JSOI2008]最小生成树计数 矩阵树定理

    题目大意: 求最小生成树的数量 曾今的我感觉这题十分的不可做 然而今天看了看,好像是个类模板的题.... 我们十分容易知道,记能出现在最小生成树中的边的集合为\(S\) 那么,只要是\(S\)中的边构 ...

  5. CF917D-Stranger Trees【矩阵树定理,高斯消元】

    正题 题目链接:https://www.luogu.com.cn/problem/CF917D 题目大意 给出\(n\)个点的一棵树,对于每个\(k\)求有多少个\(n\)个点的树满足与给出的树恰好有 ...

  6. Wannafly Camp 2020 Day 1D 生成树 - 矩阵树定理,高斯消元

    给出两幅 \(n(\leq 400)\) 个点的无向图 \(G_1 ,G_2\),对于 \(G_1\) 的每一颗生成树,它的权值定义为有多少条边在 \(G_2\) 中出现.求 \(G_1\) 所有生成 ...

  7. 洛谷P4457/loj#2513 [BJOI2018]治疗之雨(高斯消元+概率期望)

    题面 传送门(loj) 传送门(洛谷) 题解 模拟赛的时候只想出了高斯消元然后死活不知道怎么继续--结果正解居然就是高斯消元卡常? 首先有个比较难受的地方是它一个回合可能不止扣一滴血--我们得算出\( ...

  8. 【BZOJ3534】【Luogu P3317】 [SDOI2014]重建 变元矩阵树,高斯消元

    题解看这里,主要想说一下以前没见过的变元矩阵树还有前几个题见到的几个小细节. 邻接矩阵是可以带权值的.求所有生成树边权和的时候我们有一个基尔霍夫矩阵,是度数矩阵减去邻接矩阵.而所谓变元矩阵树实际上就是 ...

  9. SP104 Highways (矩阵树,高斯消元)

    矩阵树定理裸题 //#include <iostream> #include <cstdio> #include <cstring> #include <al ...

随机推荐

  1. apt-get 安装程序时报 'E: Unable to locate package xxx' 错误处理办法

    提示无法定位包,要执行命令更新: sudo apt-get update

  2. mysql各个版本驱动jar包下载 mysql/mysql-connector-java/5.1.22

    想下个jar csdn上全是要积分下载,这里记录下 下载地址,免得到时又要找 http://central.maven.org/maven2/mysql/mysql-connector-java/

  3. TDSQL-A与CK的对比

    CK介绍 CK是目前社区里面比较热门的,应用场景也比较广泛. 首先,在架构上,集群内划分为多个分片,通过分片的线性扩展能力,支持海量数据的分布式存储计算,每个分片内包含一定数量的节点Node,即进程, ...

  4. MySQL-Cluster 初识

          最近,对mysql-cluster进行初步了解,发现和oracle提供的RAC有一定的相似之处,但区别又很大,下面主要是mysql-cluster的搭建,至于对其的深入了解,留着以后工作需 ...

  5. 聊聊spring事务失效的12种场景,太坑了

    前言 对于从事java开发工作的同学来说,spring的事务肯定再熟悉不过了. 在某些业务场景下,如果一个请求中,需要同时写入多张表的数据.为了保证操作的原子性(要么同时成功,要么同时失败),避免数据 ...

  6. 理解ASP.NET Core - [03] Dependency Injection

    注:本文隶属于<理解ASP.NET Core>系列文章,请查看置顶博客或点击此处查看全文目录 依赖注入 什么是依赖注入 简单说,就是将对象的创建和销毁工作交给DI容器来进行,调用方只需要接 ...

  7. 【SpringCloud技术专题】「Eureka源码分析」从源码层面让你认识Eureka工作流程和运作机制(上)

    前言介绍 了解到了SpringCloud,大家都应该知道注册中心,而对于我们从过去到现在,SpringCloud中用的最多的注册中心就是Eureka了,所以深入Eureka的原理和源码,接下来我们要进 ...

  8. openwrt开发笔记二:树莓派刷openwrt

    前言及准备 本笔记适用于第一次给树莓派刷openwrt系统的玩家,对刷机过程及注意事项进行了记录,刷机之后对openwrt进行一些简单配置. 使用openwrt源码制作固件需要花费一点时间. 平台环境 ...

  9. 查询同一张表符合条件的某些数据的id拼接成一个字段返回

    同一张表存在类似多级菜单的上下级关系的数据,查询出符合条件的某些数据的id拼接成一个字段返回: SELECT CONCAT(a.pid, ',', b.subid) AS studentIDS FRO ...

  10. Typescript详解

    typescript由微软开发的一款开源编程语言. ts是jacascript的超集,遵循ES6,ES5规范,ts扩展了js的语法. ts更像后端java,c#这样的面向对象的语言,可以让js开发大型 ...