[专题总结]矩阵树定理Matrix_Tree及题目&题解
专题做完了还是要说两句留下什么东西的。
矩阵树定理通俗点讲就是:
建立矩阵A[i][j]=-edge(i,j),(i!=j)。即矩阵这一项的系数是两点间直接相连的边数。
而A[i][i]=deg(i)。即对角线上都是这个点的度数。
得到这个矩阵后,随便删掉一行一列后进行高斯消元得到上三角矩阵,对角线上值的积就是生成树的个数。(就是行列式)
顺便提一下行列式的性质:
交换两行/列,行列式的值变为相反数。
一行的每一项减去另一行的若干倍,行列式不变。
一行的每一项都乘一个常数,行列式也乘这个常数。
到这里就够做题了。
T1:小Z的房间:
Descpiption:
你突然有了一个大房子,房子里面有一些房间。事实上,你的房子可以看做是一个包含n*m个格子的格状矩形,每个格子是一个房间或者是一个柱子。在一开始的时候,相邻的格子之间都有墙隔着。(n,m<=10)
你想要打通一些相邻房间的墙,使得所有房间能够互相到达。在此过程中,你不能把房子给打穿,或者打通柱子(以及柱子旁边的墙)。同时,你不希望在房子中有小偷的时候会很难抓,所以你希望任意两个房间之间都只有一条通路。现在,你希望统计一共有多少种可行的方案。Mod 10^9
板子。
#include<cstdio>
#include<algorithm>
#define mod 1000000000
int ord[][],n,m,tim,A[][];char s[][];
void link(int a,int b){A[a][a]++;A[b][b]++;A[a][b]--;A[b][a]--;}
int Gauss(int ans=){
for(int i=;i<=tim;++i)for(int j=i+;j<=tim;++j)while(A[j][i]){
int d=A[i][i]/A[j][i];for(int k=i;k<=tim;++k)A[i][k]=(A[i][k]-1ll*d*A[j][k]%mod+mod)%mod;
std::swap(A[i],A[j]);ans*=-;
}for(int i=;i<=tim;++i)ans=1ll*ans*A[i][i]%mod;return (ans+mod)%mod;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;++i)scanf("%s",s[i]+);
for(int i=;i<=n;++i)for(int j=;j<=m;++j)if(s[i][j]=='.')ord[i][j]=++tim;
for(int i=;i<=n;++i)for(int j=;j<=m;++j)if(s[i][j]=='.'){
if(s[i-][j]=='.')link(ord[i-][j],ord[i][j]);
if(s[i][j-]=='.')link(ord[i][j-],ord[i][j]);
}
tim--;printf("%d\n",Gauss());
}
T2:重建:
Description:
T 国有n(n<=50)个城市,用若干双向道路连接。一对城市之间至多存在一条道路。
在一次洪水之后,一些道路受损无法通行。虽然已经有人开始调查道路的损毁情况,但直到现在几乎没有消息传回。
幸运的是,此前 T 国政府调查过每条道路的强度,现在他们希望只利用这些信息估计灾情。
具体地,给定每条道路在洪水后仍能通行的概率,请计算仍能通行的道路恰有N-1条,且能联通所有城市的概率。spj:相对误差<1e-4
变元的了。
就是边带权,要求出所有生成树边权积的和。
那么统计度数和边权的时候都不再++,--,按照边权来就可以了。
但是这道题求的没有这么简单。
求的是恰好形成一棵树,不能有多边。即求$\sum\limits_{tree} \prod\limits_{i \in tree}w_i \prod\limits_{i \notin tree}(1-w_i)$
不在树里的不好处理,所以化一下得到$\sum\limits_{tree} \prod\limits_{i \in tree}\frac{w_i}{1-w_i} \prod\limits_i(1-w_i)$
然后就得到了新的边权。
要注意设个eps,当$1-w_i$很小时除法容易锅,如果它小于eps就把它强制置为eps。
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
long double A[][];int n;
long double Gauss(){long double ans=;
for(int i=;i<n;++i){
long double mx=;int pt;
for(int j=i;j<n;++j)if(fabs(A[j][i])>mx)pt=j,mx=fabs(A[j][i]);
if(pt!=i)swap(A[pt],A[i]);
for(int j=i+;j<n;++j){
long double rate=A[j][i]/A[i][i];
for(int k=i;k<=n;++k)A[j][k]-=rate*A[i][k];
}
}
for(int i=;i<n;++i)ans*=A[i][i];return ans;
}
int main(){
scanf("%d",&n);long double pans=;
for(int i=;i<=n;++i)for(int j=;j<=n;++j)scanf("%Lf",&A[i][j]);
for(int i=;i<=n;++i)for(int j=i+;j<=n;++j)pans*=.0L-A[i][j]<1e-?1e-:.0L-A[i][j];
for(int i=;i<=n;++i)for(int j=;j<=n;++j)A[i][j]/=.0L-A[i][j]<1e-?1e-:.0L-A[i][j];
for(int i=;i<=n;++i)for(int j=;j<=n;++j)if(i!=j)A[i][i]+=A[i][j];
for(int i=;i<=n;++i)for(int j=;j<=n;++j)if(i!=j)A[i][j]=-A[i][j];
printf("%.10Lf\n",Gauss()*pans);
}
T3:2467生成树:
Description:
有一种图形叫做五角形圈。一个五角形圈的中心有1个由n个顶点和n条边组成的圈。在中心的这个n(n<=100)边圈的每一条边同时也是某一个五角形的一条边,一共有n个不同的五角形。这些五角形只在五角形圈的中心的圈上有公共的顶点。如图0所示是一个4-五角形圈。 现在给定一个n五角形圈,你的任务就是求出n五角形圈的不同生成树的数目。还记得什么是图的生成树吗?一个图的生成树是保留原图的所有顶点以及顶点的数目减去一这么多条边,从而生成的一棵树。 注意:在给定的n五角形圈中所有顶点均视为不同的顶点。mod2007
板子。注意判断n=2
#include<cstdio>
#include<algorithm>
#define mod 2007
int A[][],n;
int Gauss(int ans=){n--;
for(int i=;i<=n;++i)for(int j=i+;j<=n;++j)while(A[j][i]){
int d=A[i][i]/A[j][i];for(int k=i;k<=n;++k)A[i][k]=(A[i][k]+mod-A[j][k]*d%mod)%mod;
std::swap(A[i],A[j]);ans*=-;
}for(int i=;i<=n;++i)ans=ans*A[i][i]%mod;return (ans+mod)%mod;
}
int main(){
int t;scanf("%d",&t);
while(t--){
scanf("%d",&n);n<<=;
for(int i=;i<n;++i)for(int j=;j<n;++j)A[i][j]=;
for(int i=;i<n;++i)A[i][i]=i%==?:;
for(int i=;i<n;++i)A[i][i+]--,A[i+][i]--;
A[][n-]--,A[n-][]--;
for(int i=;i<n;i+=)A[i][i+]--,A[i+][i]--;
printf("%d\n",Gauss());
}
}
T4:黑暗前的幻想乡
Description:
四年一度的幻想乡大选开始了,最近幻想乡最大的问题是很多来历不明的妖怪涌入了幻想乡,扰乱了幻想乡昔日的秩序。但是幻想乡的建制派妖怪(人类)博丽灵梦和八云紫等人整日高谈所有妖怪平等,幻想乡多元化等等,对于幻想乡目前面临的种种大问题却给不出合理的解决方案。
风见幽香是幻想乡里少有的意识到了问题严重性的大妖怪。她这次勇敢地站了出来参加幻想乡大选,提出包括在幻想乡边境建墙(并让人类出钱),大力开展基础设施建设挽回失业率等一系列方案,成为了大选年出人意料的黑马并顺利地当上了幻想乡的大统领。
幽香上台以后,第一项措施就是要修建幻想乡的公路。幻想乡一共有n(n<=17)个城市,之前原来没有任何路。幽香向选民承诺要减税,所以她打算只修n-1条公路将这些城市连接起来。但是幻想乡有正n-1个建筑公司,每个建筑公司都想在修路地过程中获得一些好处。虽然这些建筑公司在选举前没有给幽香钱,幽香还是打算和他们搞好关系,因为她还指望他们帮她建墙。所以她打算让每个建筑公司都负责一条路来修。
每个建筑公司都告诉了幽香自己有能力负责修建的路是哪些城市之间的。所以幽香打算n-1条能够连接幻想乡所有城市的边,然后每条边都交给一个能够负责该边的建筑公司修建,并且每个建筑公司都恰好修建一条边。
幽香现在想要知道一共有多少种可能的方案呢?两个方案不同当且仅当它们要么修的边的集合不同,要么边的分配方式不同。mod1e9+7
数据范围很小,考虑枚举子集进行容斥。
奇加偶减,然后就是板子了。
#include<cstdio>
#include<algorithm>
#define mod 1000000007
int n,A[][],c[][][],ans;
int Gauss(){int ans=;
for(int i=;i<n;++i)for(int j=i+;j<n;++j)while(A[j][i]){
int d=A[i][i]/A[j][i];for(int k=i;k<n;++k)A[i][k]=(A[i][k]-1ll*A[j][k]*d%mod+mod)%mod;
std::swap(A[i],A[j]);ans*=-;
}for(int i=;i<n;++i)ans=1ll*ans*A[i][i]%mod;return ans;
}
int main(){
scanf("%d",&n);
for(int i=;i<n;++i){
int k,x,y;scanf("%d",&k);
while(k--)scanf("%d%d",&x,&y),c[i][x][x]++,c[i][y][y]++,c[i][x][y]--,c[i][y][x]--;
}
for(int st=;st<<<n-;++st){
for(int i=;i<n;++i)for(int j=;j<n;++j)A[i][j]=;
for(int l=;l<n;++l)if(st&<<l-)
for(int i=;i<n;++i)for(int j=;j<n;++j)A[i][j]+=c[l][i][j];
int pt=;for(int i=st;i;i^=i&-i)pt*=-;if(n&^)pt*=-;//printf("%d %d\n",st,pt);
ans=(0ll+ans+mod+pt*Gauss())%mod;//printf("%d\n",ans);
}printf("%d\n",ans);
}
T5:最小生成树计数
Description:
原题来自:JSOI 2008
现在给出了一个简单无向加权图。你不满足于求出这个图的最小生成树,而希望知道这个图中有多少个不同的最小生成树。mod31011(如果两颗最小生成树中至少有一条边不同,则这两个最小生成树就是不同的)。对于全部数据,n<=100,m<=1000。
数据保证不会出现自回边和重边。
注意:具有相同权值的边不会超过10条。
最后那个注意是真的要注意。。。
先来两发结论:
1。在不同的最小生成树中,每种权值的边用的数量是一定的。
2。在不同的最小生成树中,每种权值的边都加入完后,图的联通性是一定的。
(即如,你用并查集维护的话,如果每次合并都把小编号的当成根,那么得到并查集都完全一样)
仔细想一想,其实都不难证。
然后你只要状压每种边权的10条边用了哪些就好了。
也可以用matrix_tree做。但是我打的搜索。
#include<cstdio>
#include<algorithm>
#include<unordered_map>
#include<vector>
using namespace std;
unordered_map<int,int>M;
struct edge{
int a,b,v;
friend bool operator<(edge A,edge B){
return A.v<B.v;
}
}E[];
vector<edge>v[];
int n,m,f[],cnt,uc[],p[][],ans=;
int find(int k){return f[k]==k?k:f[k]=find(f[k]);}
void merge(int a,int b){if(a<b)f[b]=a;else f[a]=b;}
int main(){
scanf("%d%d",&n,&m);
for(int i=;i<=n;++i)f[i]=i;
for(int i=;i<=m;++i)scanf("%d%d%d",&E[i].a,&E[i].b,&E[i].v);
sort(E+,E++m);
for(int i=;i<=m;++i)if(M[E[i].v]!=cnt||cnt==)M[E[i].v]=++cnt;
for(int i=;i<=m;++i)E[i].v=M[E[i].v];
for(int i=;i<=m;++i)v[E[i].v].push_back(E[i]);
for(int i=;i<=cnt;++i){
for(int j=;j<v[i].size();++j)if(find(v[i][j].a)!=find(v[i][j].b))
merge(f[v[i][j].a],f[v[i][j].b]),uc[i]++;
for(int j=;j<=n;++j)p[i][j]=find(j);
}
for(int i=;i<=n;++i)if(find(i)!=){puts("");return ;}
for(int i=;i<=n;++i)p[][i]=i;
for(int i=;i<=cnt;++i){
int pl=;//printf("%d %d\n",i,pl);
for(int j=;j<<<v[i].size();++j){
for(int l=;l<=n;++l)f[l]=p[i-][l];
for(int l=;l<v[i].size();++l)if(j&<<l)
if(find(v[i][l].a)==find(v[i][l].b))goto B;
else merge(f[v[i][l].a],f[v[i][l].b]);
for(int l=;l<=n;++l)if(find(l)!=p[i][l])goto B;
pl++;B:;
}
ans=ans*pl%;//printf("%d %d\n",i,pl);
}printf("%d\n",ans);
}
T6:轮状病毒
Description:
轮状病毒有很多变种,所有轮状病毒的变种都是从一个轮状基产生的。一个N轮状基由圆环上N个不同的基原子 和圆心处一个核原子构成的,2个原子之间的边表示这2个原子之间的信息通道。如下图所示
N轮状病毒的产生规律是在一个N轮状基中删去若干条边,使得各原子之间有唯一的信息通道,例如共有16个不 同的3轮状病毒,如下图所示
现给定n(N<=100),编程计算有多少个不同的n轮状病毒
感觉少了点什么。
对,你没看错,没有模数。需要高精,__int128卡不过的。
那么直接跑Gauss得T飞吧。
所以正确的姿势是,小点matrix_tree打表找规律。大点dp写高精。
给出dp式子$dp[i]=dp[i-1]\times 3 - dp[i-2] +2$
然后就打个高精就行。
#include<cstdio>
int n;
struct bigint{
#define mod 100000000
int a[];
friend void operator+=(bigint &a,bigint b){
for(int i=;~i;--i)a.a[i]+=b.a[i];
for(int i=;i<=;++i)if(a.a[i]>=mod)a.a[i]-=mod,a.a[i+]++;
}
friend void operator-=(bigint &a,bigint b){
for(int i=;~i;--i)a.a[i]-=b.a[i];
for(int i=;i<=;++i)if(a.a[i]<)a.a[i]+=mod,a.a[i+]--;
}
void print(int i=){
for(;~i;--i)if(a[i]){printf("%d",a[i]);break;}
for(--i;~i;--i)printf("%08d",a[i]);puts("");
}
}ldp,nw,nxt;
int main(){
nw.a[]=;int n;scanf("%d",&n);n--;
while(n--){
nxt=nw;nxt+=nw;nxt+=nw;nxt-=ldp;nxt.a[]+=;
ldp=nw;nw=nxt;
}nw.print();
}
[专题总结]矩阵树定理Matrix_Tree及题目&题解的更多相关文章
- loj6271 「长乐集训 2017 Day10」生成树求和 加强版(矩阵树定理,循环卷积)
loj6271 「长乐集训 2017 Day10」生成树求和 加强版(矩阵树定理,循环卷积) loj 题解时间 首先想到先分开三进制下每一位,然后每一位分别求结果为0,1,2的树的个数. 然后考虑矩阵 ...
- 【算法】Matrix - Tree 矩阵树定理 & 题目总结
最近集中学习了一下矩阵树定理,自己其实还是没有太明白原理(证明)类的东西,但想在这里总结一下应用中的一些细节,矩阵树定理的一些引申等等. 首先,矩阵树定理用于求解一个图上的生成树个数.实现方式是:\( ...
- CF917D. Stranger Trees & TopCoder13369. TreeDistance(变元矩阵树定理+高斯消元)
题目链接 CF917D:https://codeforces.com/problemset/problem/917/D TopCoder13369:https://community.topcoder ...
- 【LOJ#6072】苹果树(矩阵树定理,折半搜索,容斥)
[LOJ#6072]苹果树(矩阵树定理,折半搜索,容斥) 题面 LOJ 题解 emmmm,这题似乎猫讲过一次... 显然先\(meet-in-the-middle\)搜索一下对于每个有用的苹果数量,满 ...
- 【bzoj4596】[Shoi2016]黑暗前的幻想乡 容斥原理+矩阵树定理
题目描述 给出 $n$ 个点和 $n-1$ 种颜色,每种颜色有若干条边.求这张图多少棵每种颜色的边都出现过的生成树,答案对 $10^9+7$ 取模. 输入 第一行包含一个正整数 N(N<=17) ...
- CSU 1805 Three Capitals(矩阵树定理+Best定理)
http://acm.csu.edu.cn/csuoj/problemset/problem?pid=1805 题意: A和B之间有a条边,A和G之间有b条边,B和G之间有c条边.现在从A点出发走遍所 ...
- BZOJ 2467: [中山市选2010]生成树(矩阵树定理+取模高斯消元)
http://www.lydsy.com/JudgeOnline/problem.php?id=2467 题意: 思路:要用矩阵树定理不难,但是这里的话需要取模,所以是需要计算逆元的,但是用辗转相减会 ...
- BZOJ 1002 轮状病毒 矩阵树定理
题目链接: https://www.lydsy.com/JudgeOnline/problem.php?id=1002 题目大意: 给定n(N<=100),编程计算有多少个不同的n轮状病毒 思路 ...
- 【bzoj5133】[CodePlus2017年12月]白金元首与独舞 并查集+矩阵树定理
题目描述 给定一个 $n\times m$ 的方格图,每个格子有 ↑.↓.←.→,表示从该格子能够走到相邻的哪个格子.有一些格子是空着的,需要填上四者之一,需要满足:最终的方格图中,从任意一个位置出发 ...
随机推荐
- ELK 学习笔记之 Logstash安装
Logstash安装: https://www.elastic.co/downloads/logstash 下载解压: tar –zxvf logstash-5.6.1.tar.gz 在/usr/lo ...
- 从0开始学FreeRTOS-(消息队列)-5
## 问题解答 曾经有人问我,FreeRTOS那么多API,到底怎么记住呢? 我想说,其实API不难记,就是有点难找,因为FreeRTOS的API很多都是带参宏,所以跳来跳去的比较麻烦,而且注释也很多 ...
- 洛谷P2051 [AHOI2009] 中国象棋(状压dp)
题目简介 n*m的棋盘,对每行放炮,要求每行每列炮数<=2,求方案数%9999973 N,M<=100 题目分析 算法考虑 考虑到N,M范围较小,每一行状态只与前面的行状态有关,考虑状压D ...
- QT文件读写操作笔记
补一下这部分的笔记 简单的东西也记一下 操作系统一般都会提供一些列的标准对话框,如文件选择.字体选择.颜色选择等,这些标准对话框为应用层序提供了一致的观感.Qt对这些标准对话框都定义了相关的类,如:Q ...
- 域渗透-msdtc实现dll劫持后门
最近用的多 一个实用小tips 文章参考原创Shadow Force大牛 翻译文章参考三好大佬 利用MSDTC服务加载后门dll,实现自启动后门 后门思路可以查看趋势科技文章 https://bl ...
- 给iOS中高级求职者的一份面试题解答
前段时间更新了一篇 给iOS中高级面试官的一份招聘要求 收到很多小伙伴的点赞与关注.可能有很多小伙伴已经带着我在那篇文章给大家提供的一些面试技巧 & 其中的面试题 已经开始招聘或者应聘了!这里 ...
- 一次看懂 Https 证书认证
TLS 传输层安全性协定 TLS(Transport Layer Security),及其前身安全套接层 SSL(Secure Sockets Layer)是一种安全协议,目的是为网际网路通信,提供安 ...
- Spring容器启动源码解析
1. 前言 最近搭建的工程都是基于SpringBoot,简化配置的感觉真爽.但有个以前的项目还是用SpringMvc写的,看到满满的配置xml文件,却有一种想去深入了解的冲动.折腾了好几天,决心去写这 ...
- java学习5-面向对象(下)
final修饰符: final用于修饰类.变量和方法. final修饰变量时,一旦获得了初始值就不可改变 1.抽象方法和抽象类 抽象方法与抽象类的规则: a.抽象方法和抽象类必须使用abstract修 ...
- python类中的self
class User: def walk(self): print(self,"正在慢慢走") # User.walk() # 会报错 TypeError: walk() miss ...