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. docker-compose权限不够

    root@kali:~# docker-compose version -bash: /usr/local/bin/docker-compose: 权限不够 chmod +x /usr/local/b ...

  2. Ubuntu16.04 Linux 下无痛安装、配置Gogs

    本文在Win7+VMware的ubuntu 16.04中测试,安装Gogs,Install from binary. 准备工作: sudo apt-get install git sudo addus ...

  3. linux grep命令使用详解

    grep是我们最常用的命令之一,但是正真用的熟的不多,把基础命令记牢固,能帮我们节约很多时间 grep的option -A 1 表示找到所有匹配行,并显示所有匹配行后的一行 在错误日志查找时还是很有用 ...

  4. (三)羽夏看C语言——进制

    写在前面   由于此系列是本人一个字一个字码出来的,包括示例和实验截图.本人非计算机专业,可能对本教程涉及的事物没有了解的足够深入,如有错误,欢迎批评指正. 如有好的建议,欢迎反馈.码字不易,如果本篇 ...

  5. DSP开发笔记一

    前言 ​ 本笔记首先对DSP的特点及其选型进行了描述,然后重点记录DSP开发环境的搭建及基础工程示例,对为DSP开发新手有一定的指导作用. 1. DSP简介 1.1 主要特点 在一个指令周期内可完成一 ...

  6. LeetCode刷题模板(1):《我要打10个》之二分法

    Author       :  叨陪鲤 Email         : vip_13031075266@163.com Date          : 2021.01.23 Copyright : 未 ...

  7. VMware Vsphere 虚拟化

    总体架构 主要组件: 1)ESXi 底层虚拟化层,用于将物理服务器虚拟成资源池,提供管理接口,方便其他的管理组件进行管理,其实体形态是iso文件,刻成启动光盘可直接安装在服务器裸机上: 安装在实体服务 ...

  8. 学习Tomcat(二)之容器概览

    Tomcat容器的Server模块有管理容器的启动和关闭.管理了容器内的服务组件Service.管理了全局JNDI资源的功能,对Tomcat容器的生命周期管理有重要意义.Tomcat的服务组件则是To ...

  9. expression动态构成

    http://blog.csdn.net/tastelife/article/details/7340205 http://blog.csdn.net/tastelife/article/detail ...

  10. Python小技巧:这17个骚操作你都OK吗?

    导读:Python 是一门非常优美的语言,其简洁易用令人不得不感概人生苦短.本文中带我们回顾了 17 个非常有用的 Python 技巧,例如查找.分割和合并列表等.这 17 个技巧都非常简单,但它们都 ...