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

Solution

很容易想到,设 \(G_1\) 中每条边的权值,这条边在 \(G_2\) 中出现则权值为 \(1\),否则权值为 \(0\)。

现在就真的是求所有生成树的边权和的权值和了。

然而标准的 Matrix-Tree Theorem 求的是生成树的边权积的和。

现在我们定义每条只出现在 \(G_1\) 中的边边权为 \(1\),同时出现在 \(G_1,G_2\) 中的边权为 \(x\),则基尔霍夫矩阵的每个元素嗾使一个多项式,记为 \(B(x)\)。

\(\det B(x)\) 是一个 \(n-1\) 次多项式 \(f(x) = \sum a_i x^i\),那么其中 \(a_i\) 就是使用了 \(i\) 条公共变的生成树个数。

于是答案就是 \(f'(1)=\sum ia_i=\det B(1) \cdot \sum_i \sum_j (B^{-1}(1))_{i,j}\cdot B'(1)_{i,j}\)

于是用 Gauss 消元法求行列式和逆矩阵即可

#include <bits/stdc++.h>
using namespace std; #define int long long
const int N = 405;
const int mod = 998244353; namespace mat {
int f[N][N<<1],a[N][N],n;
inline void exgcd(int a,int b,int &x,int &y) {
if(!b) {
x=1,y=0;
return;
}
exgcd(b,a%b,x,y);
int t=x;
x=y,y=t-(a/b)*y;
} inline int inv(int a,int b) {
int x,y;
return exgcd(a,b,x,y),(x%b+b)%b;
}
int getdet() {
int det=1;
int flag=0;
for(int i=1; i<=n; i++) {
for(int j=i+1; j<=n; j++) {
int x=i,y=j;
while(a[y][i]!=0) {
int t=a[x][i]*inv(a[y][i],mod)%mod;
for(int k=i; k<=n; k++) (a[x][k]-=t*a[y][k]%mod)%=mod;
swap(x,y);
}
if(x!=i) {
for(int k=1; k<=n; k++) {
swap(a[x][k],a[i][k]);
}
flag^=1;
}
}
if(a[i][i]==0) return 0;
det=det*a[i][i]%mod;
}
if(flag) det=-det;
det%=mod; det+=mod; det%=mod;
return det;
} int solve() {
int m=n*2;
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) a[i][j]=f[i][j], f[i][j+n]=0;
}
int ret= getdet();
for(int i=1; i<=n; ++i) {
f[i][n+i]=1;
}
for(int i=1; i<=n; ++i) {
for(int j=i; j<=n; j++)
if(f[j][i]) {
for(int k=1; k<=m; k++)
swap(f[i][k],f[j][k]);
break;
}
if(!f[i][i]) {
return 0;
}
int r=inv(f[i][i],mod);
for(int j=i; j<=m; ++j)
f[i][j]=f[i][j]*r%mod;
for(int j=1; j<=n; ++j)
if(j!=i) {
r=f[j][i];
for(int k=i; k<=m; ++k)
f[j][k]=(f[j][k]-r*f[i][k]%mod+mod)%mod;
}
}
return ret;
}
} int n,b[N][N],bd[N][N];
char g1[N][N],g2[N][N]; signed main() {
cin>>n;
for(int i=1;i<=n;i++) {
cin>>g1[i]+1;
}
for(int i=1;i<=n;i++) {
cin>>g2[i]+1;
}
for(int i=1;i<=n;i++) {
for(int j=1;j<i;j++) {
if(g1[i][j]=='1') b[i][j]=-1, b[j][i]=-1, b[i][i]++, b[j][j]++;
if(g1[i][j]=='1' && g2[i][j]=='1') bd[i][j]=-1, bd[j][i]=-1, bd[i][i]++, bd[j][j]++;
}
}
for(int i=1;i<n;i++) {
for(int j=1;j<n;j++) {
mat::f[i][j]=b[i][j];
}
}
--n;
mat::n=n;
int det=mat::solve();
int ans=0;
/*for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) cout<<b[i][j]<<" ";
cout<<endl;
}
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) cout<<bd[i][j]<<" ";
cout<<endl;
}
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) cout<<mat::f[i][j+n]<<" ";
cout<<endl;
}*/
for(int i=1;i<=n;i++) {
for(int j=1;j<=n;j++) {
ans+=mat::f[i][j+n]*bd[i][j];
ans%=mod;
ans+=mod;
ans%=mod;
}
}
//cout<<ans<<" "<<det<<endl;
cout<<((ans*det)%mod+mod)%mod;
}

Wannafly Camp 2020 Day 1D 生成树 - 矩阵树定理,高斯消元的更多相关文章

  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. CF917D-Stranger Trees【矩阵树定理,高斯消元】

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

  5. 洛谷4208 JSOI2008最小生成树计数(矩阵树定理+高斯消元)

    qwq 这个题目真的是很好的一个题啊 qwq 其实一开始想这个题,肯定是无从下手. 首先,我们会发现,对于无向图的一个最小生成树来说,只有当存在一些边与内部的某些边权值相同的时候且能等效替代的时候,才 ...

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

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

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

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

  8. 【bzoj2467】[中山市选2010]生成树 矩阵树定理

    题目描述 有一种图形叫做五角形圈.一个五角形圈的中心有1个由n个顶点和n条边组成的圈.在中心的这个n边圈的每一条边同时也是某一个五角形的一条边,一共有n个不同的五角形.这些五角形只在五角形圈的中心的圈 ...

  9. Wannafly挑战赛23F-计数【原根,矩阵树定理,拉格朗日插值】

    正题 题目链接:https://ac.nowcoder.com/acm/contest/161/F 题目大意 给出\(n\)个点的一张图,求它的所有生成树中权值和为\(k\)的倍数的个数.输出答案对\ ...

随机推荐

  1. 20191228--python学习第四天

    今日内容: 列表 元组 内容回顾与补充 1.计算机基础 硬件:CPU/内存/硬盘/主板/网卡 操作系统:linux(免费/开源) centos/ubuntu/redhat   windows   ma ...

  2. 根据map中的某一key进行排序(快速排序实现)

    /** * @author Gaoxl * 根据key值排序,要求key值能够转为Long类型(快速排序) * @param maps * @param key * @return */ public ...

  3. ModbusTCP协议解析 —— 利用Wireshark对报文逐字节进行解析详细解析Modbus所含功能码

    现在网上有很多类似的文章.其实这一篇也借鉴了很多其他博主的文章. 写这篇文章的重点是在于解析功能和报文.对Modbus这个协议并不会做很多介绍. 好了,我们开始吧. 常用的功能码其实也没多少.我也就按 ...

  4. 进阶之路 | 奇妙的Animation之旅

    前言 本文已经收录到我的Github个人博客,欢迎大佬们光临寒舍: 我的GIthub博客 学习清单: 动画的种类 自定义View动画 View动画的特殊使用场景 属性动画 使用动画的注意事项 一.为什 ...

  5. Mybatis注解开发多表一对一,一对多

    Mybatis注解开发多表一对一,一对多 一对一 示例:帐户和用户的对应关系为,多个帐户对应一个用户,在实际开发中,查询一个帐户并同时查询该账户所属的用户信息,即立即加载且在mybatis中表现为一对 ...

  6. scrapy 当当网 爬虫

    前言 好久没有写实战博客了,因为前几个月在公司实习,博客更新就耽搁了下来,现在又受疫情影响无法返校,但是技能还是不能丢的,今天就写一篇使用scrapy爬取当当网的实战练习吧. 创建scrapy项目 目 ...

  7. djinn:1 Vulnhub Walkthrough

    靶机下载链接: https://download.vulnhub.com/djinn/djinn.ova 主机端口扫描: FTP发现一些文件提示 1337端口是一个游戏,去看下 哈哈有点难,暂时放弃, ...

  8. Objective-C编程 — 类和继承

    讲述面向对象中的一个重要概念——继承,使用继承 可以方便地在已有类的基础上进行扩展,定义一个具有父 类全部功能的新类. 父类和子类 我们在定义一个新类的时候,经常会遇到要定义的新类是某个类的扩展或者是 ...

  9. CentOS6 用yum安装mysql详解,简单实用

    一.查看CentOS下是否已安装mysql 输入命令 :yum list installed | grep mysql 二.删除已安装mysql 输入命令: yum -y remove mysql 如 ...

  10. Linux系统开机显示BusyBox v1.22.1 built-in shell(ash) 解决方法

    BusyBox 是一个集成了三百多个最常用Linux命令和工具的软件.BusyBox 包含了一些简单的工具,例如ls.cat和echo等等,还包含了一些更大.更复杂的工具,例grep.find.mou ...