洛谷4208 JSOI2008最小生成树计数(矩阵树定理+高斯消元)
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最小生成树计数(矩阵树定理+高斯消元)的更多相关文章
- [spoj104][Highways] (生成树计数+矩阵树定理+高斯消元)
In some countries building highways takes a lot of time... Maybe that's because there are many possi ...
- BZOJ4031 [HEOI2015]小Z的房间 【矩阵树定理 + 高斯消元】
题目链接 BZOJ4031 题解 第一眼:这不裸的矩阵树定理么 第二眼:这个模\(10^9\)是什么鬼嘛QAQ 想尝试递归求行列式,发现这是\(O(n!)\)的.. 想上高斯消元,却又处理不了逆元这个 ...
- P3317 [SDOI2014]重建 变元矩阵树定理 高斯消元
传送门:https://www.luogu.org/problemnew/show/P3317 这道题的推导公式还是比较好理解的,但是由于这个矩阵是小数的,要注意高斯消元方法的使用: #include ...
- luoguP4208 [JSOI2008]最小生成树计数 矩阵树定理
题目大意: 求最小生成树的数量 曾今的我感觉这题十分的不可做 然而今天看了看,好像是个类模板的题.... 我们十分容易知道,记能出现在最小生成树中的边的集合为\(S\) 那么,只要是\(S\)中的边构 ...
- CF917D-Stranger Trees【矩阵树定理,高斯消元】
正题 题目链接:https://www.luogu.com.cn/problem/CF917D 题目大意 给出\(n\)个点的一棵树,对于每个\(k\)求有多少个\(n\)个点的树满足与给出的树恰好有 ...
- Wannafly Camp 2020 Day 1D 生成树 - 矩阵树定理,高斯消元
给出两幅 \(n(\leq 400)\) 个点的无向图 \(G_1 ,G_2\),对于 \(G_1\) 的每一颗生成树,它的权值定义为有多少条边在 \(G_2\) 中出现.求 \(G_1\) 所有生成 ...
- 洛谷P4457/loj#2513 [BJOI2018]治疗之雨(高斯消元+概率期望)
题面 传送门(loj) 传送门(洛谷) 题解 模拟赛的时候只想出了高斯消元然后死活不知道怎么继续--结果正解居然就是高斯消元卡常? 首先有个比较难受的地方是它一个回合可能不止扣一滴血--我们得算出\( ...
- 【BZOJ3534】【Luogu P3317】 [SDOI2014]重建 变元矩阵树,高斯消元
题解看这里,主要想说一下以前没见过的变元矩阵树还有前几个题见到的几个小细节. 邻接矩阵是可以带权值的.求所有生成树边权和的时候我们有一个基尔霍夫矩阵,是度数矩阵减去邻接矩阵.而所谓变元矩阵树实际上就是 ...
- SP104 Highways (矩阵树,高斯消元)
矩阵树定理裸题 //#include <iostream> #include <cstdio> #include <cstring> #include <al ...
随机推荐
- 快速使用 Docker 上手 Sentry-CLI - 玩转 Source Maps 使用 (create-react-app)
系列 快速使用 Docker 上手 Sentry-CLI - 创建版本 入门 使用 sentry-cli 上传 source maps 时,您需要设置构建系统以创建版本(release)并上传与该版本 ...
- 如何攻击Java Web应用
越来越多的企业采用Java语言构建企业Web应用程序,基于Java主流的框架和技术及可能存在的风险,成为被关注的重点. 本文从黑盒渗透的角度,总结下Java Web应用所知道的一些可能被利用的入侵点. ...
- grpc服务发现与负载均衡
前言 在后台服务开发中,高可用性是构建中核心且重要的一环.服务发现(Service discovery)和负载均衡(Load Balance)一直都是我关注的话题.今天来谈一下我在实际中是如何理解及落 ...
- Android kotlin http url request
kotlin.concurrent.thread{ val url = "https://hangj.cnblogs.com/" val res = try { java.net. ...
- Typora代码块配色和标题自带序号的实现代码
Typora代码块配色和标题自带序号的实现代码 先打开主题文件夹 文件>偏好设置>外观>打开主题文件夹 然后编辑base.user.css(如果没有就新建一个)文件 /*标题自动添加 ...
- 剑指 Offer 38. 字符串的排列
剑指 Offer 38. 字符串的排列 输入一个字符串,打印出该字符串中字符的所有排列. 你可以以任意顺序返回这个字符串数组,但里面不能有重复元素. 示例: 输入:s = "abc" ...
- ubantu下载源详细目录
都说ubantu系统自带的下载源不给力,一般使用时体现不出来,也没有必要更换.我是在安装gnuradio时,安装了好久,没安装上,后来就去更改下载源(后来发现不是下载源的问题),不过还不错,最起码最下 ...
- 全局CSS样式表
看api手册使用即可 1.按钮和图片 2.表格.表单 表单的lable作用就是点击前面的文字可以聚焦到对应的输入框中
- Vue组件传值(一)之 父子之间如何传值
Vue中组件之间是如何实现通信的? 1.父传子: 父传子父组件通过属性进行传值,子组件通过 props 进行接受: 1 父组件中: 2 3 <template> 4 <div id= ...
- Java集合:HashMap
Hashmap是一个存储key-value的映射表. 优点: 索引数据快,查找一个数据对的时间复杂度是O(1) 增加.删除一个数据的时间复杂度是O(1) key不能重复,可以存储一个null值 存储: ...