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. Ubuntu16.04 Linux 下安装、配置SSH

    本人在Win7+VMware下利用两个ubuntu虚拟机安装.配置.测试了SSH. 在Server端安装openssh-server. sudo apt-get install ssh # 安装ssh ...

  2. 24点游戏(24 game)的C++编程求解实现

    什么是24点游戏 24点游戏,英文叫做24 game,是对给定的4个非负整数进行加减乘除运算,要求每个数都要被用到且仅用到一次,并得到最终的运算结果为24.比如3.8.3.8这四个数,可以找出唯一的一 ...

  3. SSE图像算法优化系列三十一:Base64编码和解码算法的指令集优化。

        一.基础原理 Base64是一种用64个Ascii字符来表示任意二进制数据的方法.主要用于将不可打印的字符转换成可打印字符,或者简单的说是将二进制数据编码成Ascii字符.Base64也是网络 ...

  4. seo高手教你seo优化排名该怎么做

    seo高手教你seo优化排名该怎么做 第一节:如何在本地搭建服务器环境 本节课程主要是讲如何利用 Xampp在本地搭建服务器环境 .网站使用asp和php比较常见,当然,就目前而言,使用php搭建网站 ...

  5. 类的基础语法阅读【Python3.8官网文档】

    英文官方文档: https://docs.python.org/3.8/tutorial/classes.html 中文官方文档: https://docs.python.org/zh-cn/3.8/ ...

  6. Android常见面试题(一)

    ANDROID(一) Activity 1.什么是Activity? 请描述一下生命周期 Activity: 一个Activity是一个应用程序组件,提供一个屏幕,用户可以用来交互为了完成某项任务,例 ...

  7. Lambda@edge 实现负载均衡器功能

    一般的业务实现流程为CDN->ELB->EC2,但OTT业务往往会产生很高的流量费用,如果使用常规的架构,流量费用会成倍增加,为了降低费用,我们对架构做了一些优化. AWS Cloudfr ...

  8. 如何实现LRU缓存?

    面试官:来了,老弟,LRU缓存实现一下? 我:直接LinkedHashMap就好了. 面试官:不要用现有的实现,自己实现一个. 我:..... 面试官:回去等消息吧.... 大家好,我是程序员学长,今 ...

  9. 性能测试工具JMeter 基础(五)—— 测试元件: 测试计划

    测试计划的定义: 测试计划是测试脚本的容器,定义了要执行什么.怎么执行对测试做总体的设置,且都是从线程组开始执行 在测试计划中可自定义用户变量(User Defined Variables),可通过A ...

  10. VS Code 搭建stm32开发环境

    MCU免费开发环境 一般芯片厂家会提供各种开发IDE方案,通常其中就包括其自家的集成IDE,如: 意法半导体 STM32CubeIDE NXP Codewarrior TI CCS 另外也可以用ecl ...