Cayley 定理

节点个数为 \(n\) 的无根标号树的个数为 \(n^{n−2}\) 。

这个结论在很多计数类题目中出现,要证明它首先需要了解 \(\text{Prufer}\) 序列的相关内容。接下来给出证明。

证明:

每一棵树都可以转换为一个 \(\text{Prufer}\) 序列。

根据定义,每一个节点在 \(\text{Prufer}\) 序列中出现的次数等于该节点度数减一,即 \(d_i–1\)。整个 \(\text{Prufer}\) 序列的长度为 \(∑_id_i–1=2(n−1)–n=n–2\) 。

无根树的形态可以与 \(\text{Prufer}\) 序列一一对应,所以无根树的个数也等于 \(\text{Prufer}\) 序列的个数。故个数为 \(n−2\) 。

扩展 Cayley 定理 1

\(n\) 个标号节点形成一个有 \(s\) 颗树的森林且给定的 \(s\) 个点没有两个点属于同一颗树的方案数个数为 \(sn^{n-s-1}\) 。

证明:记其为 \(F(n,s)\)(\(1\le s\le n\)),其中被钦定的 \(s\) 个不在同一棵树的点的标号是 \([n-s+1,n]\) 。枚举 \(n\) 的度数 \(j\) ,则得到以下递推式:

\[F(n,s)=\sum_{j=0}^{n-s}\binom{n-s}{j}F(n-1,s-1+j)
\]

下面使用数学归纳法来证明 \(F(n,s)=sn^{n-s-1}\) :

\(n=1\) 时显然成立。

当 \(n>1\) 时,若 \(n-1\) 成立(即对于 \(1\le k\le n-1\),\(F(n-1,k)=k(n-1)^{n-1-k}\) ,则有:

\[F(n,s)=\sum_{j=0}^{n-s}\binom{n-s}{j}(s-1+j)(n-1)^{n-s-j-1}
\\
=\frac{1}{n-1}\sum_{j=0}^{n-s}\binom{n-s}{j}(s-1)(n-1)^{n-s-j}+\frac{1}{n-1}\sum_{j=0}^{n-s}\binom{n-s}{j}j(n-1)^{n-s-j}
\\
=\frac{s-1}{n-1}\sum_{j=0}^{n-s}\binom{n-s}{j}(n-1)^{n-s-j}+\frac{n-s}{n-1}\sum_{j=0}^{n-s}\binom{n-s-1}{j-1}(n-1)^{n-s-j}
\\
=\frac{s-1}{n-1}n^{n-s}+\frac{n-s}{n-1}n^{n-s-1}=\frac{sn-s}{n-1}n^{n-s-1}=sn^{n-s-1}
\]

得证。

组合解释:

把给定的 \(s\) 个点看成被钦定的根结点,先当作没给定,然后再除以 \(\Large\binom{n}{s}\) 。加一个虚点 \(0\),把所有根连成它的儿子,变成 \(n+1\) 个点。钦定 \(0\) 的度数为 \(s\) 的树的个数。

\(\text{prufer}\) 序列的长度是 \(n-1\),钦定其中有 \(s-1\) 个是 \(0\),剩下的是 \(1,2,\cdots , n\) 。则方案数是 \({\large\binom{n-1}{s-1}}\times n^{n-s}\) 。然后再除以 \(\Large\binom{n}{s}\) ,得到 \(sn^{n-s-1}\) 。

扩展 Cayley 定理 2

对于一棵 \(n\) 个节点有标号无根树,已经被若干条边分成了大小分别为的若干连通块 \(a_1,a_2,\cdots, a_m\),连成一棵树的方案数为 \(\prod a_i \times n^{m-2}\) 。

用 \(\text{Matrix-Tree}\) 定理证明:

将每一个连通块压在一起考虑,此时有连通块 \(a_i,a_j\) 之间的边数即为 \(a_i\cdot a_j\) 。

那么有矩阵如下:

\[\left(\begin{array}{ccccc} a_1(n-a_1)& -a_1a_2 & -a_1a_3 & \cdots & -a_1a_m\\ -a_2a_1& a_2(n-a_2) & -a_2a_3 & \cdots & -a_2a_m \\ & & \ddots & & \\ -a_{m-1}a_1 & -a_{m-1} a_2 & -a_{m-1}a_3 & \cdots & -a_{m-1}a_m\\ -a_{m}a_1 & -a_{m} a_2 & -a_{m}a_3 & \cdots & a_m(n-a_m)\end{array}\right)
\]

去掉最后一行,最后一列,得到:

\[\left(\begin{array}{ccccc} a_1(n-a_1)& -a_1a_2 & -a_1a_3 & \cdots & -a_1a_{m-1}\\ -a_2a_1& a_2(n-a_2) & -a_2a_3 & \cdots & -a_2a_{m-1} \\ & & \ddots & & \\ -a_{m-1}a_1 & -a_{m-1} a_2 & -a_{m-1}a_3 & \cdots & a_{m-1}(n-a_{m-1})\end{array}\right)
\]

接下来要算其行列式,首先化简矩阵,第 \(i (i>1)\) 行减去第 \(i-1\) 行 \(\times \dfrac{a_i}{a_{i-1}}\) 。

得到新的矩阵:

\[\left(\begin{array}{cccccc} a_1(n-a_1)& -a_1a_2 & -a_1a_3 & \cdots & -a_1a_{m-2} & -a_1a_{m-1}\\ -a_2n& a_2n & 0 & \cdots & 0& 0 \\ & & & \ddots & & \\ 0 & 0 & 0 & \cdots & -a_{m-1}n & a_{m-1}n\end{array}\right)
\]

此时,除了第一行以外的每一行,只在 \(i-1,i\) 处有值,这样的特殊矩阵,带入行列式的表达式

\[\displaystyle \det(A)=\sum_{\text{p is a permutation}} (-1)^{N(p)} \prod A_{i,p_i}
\]

有值的 \(p\) 只有 \(n\) 个,分成两类:

  1. \(p_i=i,N(p)=0\),\(\prod A_{i,p_i}=(n-a_1)n^{m-2} \prod_{i<m}\limits a_i\)

  2. \(p_1=q(q>1),p_j=\left\{\begin{aligned} j-1 && 2\leq j\leq q\\ j && q<j\end{aligned}\right.\)

\[\prod A_{i,p_i}=(-1)^{q-1} (-n)^{q} n^{m-1-q}a_q \prod_{i<m}\limits a_i=-n^{m-2} a_q\prod_{i<m}\limits a_i
\]

则:

\[\det=(n-a_1-a_2-\cdots-a_{m-1})n^{m-2} \prod_{i<m}\limits a_i=a_mn^{m-2} \prod_{i<m}\limits a_i=n^{m-2} \prod a_i
\]

即方案数为 \(n^{m-2} \prod a_i\) 。

CF1109D Sasha and Interesting Fact from Graph Theory

给定参数 \(n,m,a,b\) ,你现在要构造一颗 \(n\) 个点树,树边的权值可以赋为 \([1,m]\) 中的一个整数。

求有多少种构造树的方法,使得节点 \(a\) 与节点 \(b\) 在树上的最短路径恰好为 \(m\) ,答案对 \(10^9+7\) 取模。

然后枚举 \(a\) 和 \(b\) 之间的边数 \(e\)。首先,除了 \(a\) 和 \(b\) 两个点以外有 \(n-2\) 个点,从中选出 \(e-1\) 个排成 \(a\) 到 \(b\) 中间的点,方案数是 \((n-2) ^ {\underline{e-1}}\) 。

\(a\) 到 \(b\) 权值和为 \(m\) 且每个边权都在 \([1,m]\) 中,所以是在 \(a\) 到 \(b\) 之间插板,方案数是 \(\binom{m-1}{e-1}\) 。

剩下的 \(n-e-1\) 个数的权值可以任取,则方案数是 \(m^{n-e-1}\) 。因为是这条链上 \(e+1\) 个点都挂一棵树,所以再乘上 \(F(n,e+1)\) 。

答案即为:

\[\sum_{e=1}^{n-1} (n-2) ^ {\underline{e-1}}\times \binom{m-1}{e-1}\times m^{n-e-1}\times F(n,e+1)
\]
点击查看代码
#include <bits/stdc++.h>
using namespace std;
int n, m, a, b;
const int md = 1e9 + 7;
int fac[1000005], inv[1000005], F[1000005];
inline int pwr(int x, int y) {
int res = 1; y = (y + md - 1) % (md - 1);
while (y) {
if (y & 1) res = 1ll * res * x % md;
x = 1ll * x * x % md; y >>= 1;
}
return res;
}
inline void init() {
fac[0] = fac[1] = inv[0] = inv[1] = 1;
for (int i = 2; i <= 1e6; i++) fac[i] = 1ll * fac[i - 1] * i % md;
for (int i = 2; i <= 1e6; i++) inv[i] = 1ll * (md - md / i) * inv[md % i] % md;
for (int i = 2; i <= 1e6; i++) inv[i] = 1ll * inv[i] * inv[i - 1] % md;
for (int i = 1; i <= 1e6; i++) F[i] = 1ll * i * pwr(n, n - i - 1) % md;
}
inline int C(int x, int y) {
if (x < y) return 0;
return 1ll * fac[x] * inv[y] % md * inv[x - y] % md;
}
int main() {
scanf("%d%d%d%d", &n, &m, &a, &b);
init();
int res = 0;
for (int i = 1; i < n; i++) {
int tmp = 1ll * fac[n - 2] * inv[n - 1 - i] % md * C(m - 1, i - 1) % md * pwr(m, n - i - 1) % md * F[i + 1] % md;
res = (res + tmp) % md;
}
printf("%d\n", res); return 0;
}

「NOI2022模拟赛czx2」图

给定一张图 \(G = (V = {1, 2, · · · , n}, E = {(u_1, v_1), (u_2, v_2), · · · , (u_m, v_m)})\)。

先将其复制 \(k\) 份,得到一张 \(nk\) 个点,\(mk\) 条边的图 \(G′\),设其补图为 \(H\) ,求 \(H\) 的生成树数量 \(\pmod P\)。

考虑进行容斥,强制出现 \(G\) 中的若干条边,对于剩下的部分,由 \(\text{Ex-Cayley}\) 定理可知,答案为 \(∏ s_i· (nk)^{c−2}\) ,其中 \(s_i\) 表示每一个连通块的大小,\(c\) 表示连通块的数量。

若能求出 \(G\) 中生成 \(i\) 个连通块的答案,添加 \((nk)^i\) 的系数后做 \(k\) 次幂,就也能求出 \(G′\) 的总答案。

考虑使用 \(\text{Matrix-Tree}\) 定理求解 \(G′\) 的答案,设:\(f_c =∑ ∏ s_i· (nk)^c\)

添加 \(0\) 号点,\(0\) 向每一个节点连接一条权为 \(x\) 的边,求出生成树。

则每一个连通块有 \(s_i\) 种向根节点连边的方案,且每一个连通块会使得最终的总权值 \(×x\) 。

即求出的行列式为一个多项式 \(f(x)\),\([x^c]f(x) = f_c\) ,观察可以发现,我们并不需要知道整个多项式,只需要知道 \(∑(−nk)^if_i\) ,即 \(f(−nk)\) 即可。

再考虑模数不是质数的情况,高斯消元可以采用辗转相除法,复杂度依然为 \(O(n^4)\) ,而算法 \(2\) 和算法 \(3\) 在计算的最后均需要添加系数 \((nk)^{−2}\),而 \(nk\) 可能不存在逆元。

容易发现 \(f(x)\) 并不存在常数项,因此可以将 \(i\to 0\) 的边权设为 \(1\) ,而 \(0\to i\) 的边权仍为 \(x\) 。

再将根节点设为 \(0\) 以外的点即可求解出 \(\frac f x(−nk)\),最后添加 \((nk)^{k−2}\) 作为系数即可(\(k = 1\) 时需要特判)。

点击查看代码
#include <bits/stdc++.h>
using namespace std;
int n, m, k, md;
inline int pwr(int x, int y) {
int res = 1;
while (y) {
if (y & 1)
res = 1ll * res * x % md;
x = 1ll * x * x % md;
y >>= 1;
}
return res;
}
struct mat {
int a[105][105];
inline int* operator[](int t) { return a[t]; }
inline int det() {
int res = 1;
for (int i = 0; i < n; i++) {
int loc = i;
for (int j = n - 1; j >= i; j--) if (a[j][i]) loc = j;
if (loc != i) swap(a[i], a[loc]), res = -res;
if (!a[i][i]) return 0;
for (int j = i + 1; j < n; j++) {
while (a[j][i]) {
int tmp = a[i][i] / a[j][i];
for (int t = i; t < n; t++) a[i][t] = (a[i][t] - 1ll * tmp * a[j][t]) % md;
swap(a[i], a[j]); res = -res;
}
}
}
for (int i = 0; i < n; i++) res = 1ll * res * a[i][i] % md;
return (res + md) % md;
}
} mp;
int main() {
freopen("graph.in", "r", stdin);
freopen("graph.out", "w", stdout);
scanf("%d%d%d%d", &n, &m, &k, &md);
if (k == 1) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (i == j) mp[i][j] = n - 1;
else mp[i][j] = -1;
}
}
for (int i = 1; i <= m; i++) {
int x, y;
scanf("%d%d", &x, &y);
mp[x - 1][x - 1]--; mp[y - 1][y - 1]--;
mp[x - 1][y - 1] = mp[y - 1][x - 1] = 0;
}
n--;
printf("%d\n", mp.det());
} else {
for (int i = 1; i <= m; i++) {
int x, y; scanf("%d%d", &x, &y);
mp[x][x]++; mp[y][y]++;
mp[x][y] = mp[y][x] = -1;
}
int nk = 1ll * n * k % md;
for (int i = 1; i <= n; i++) {
mp[0][0] = (mp[0][0] - 1) % md;
mp[i][i] = (mp[i][i] - nk) % md;
mp[i][0] = (mp[i][0] + nk) % md;
mp[0][i] = (mp[0][i] + 1) % md;
}
int res = pwr(mp.det() * (n & 1 ? md - 1ll : 1ll) % md, k);
printf("%lld\n", 1ll * res * pwr(nk, k - 2) % md);
} return 0;
}

Cayley 定理与扩展 Cayley 定理的更多相关文章

  1. Lucas定理和扩展Lucas定理

    1.Lucas定理 首先给出式子:\(C_n^m\%p = C_{\lfloor\frac{n}{p}\rfloor}^{\lfloor\frac{m}{p}\rfloor} * C_{n\%p}^{ ...

  2. 2015 ICL, Finals, Div. 1 Ceizenpok’s formula(组合数取模,扩展lucas定理)

    J. Ceizenpok’s formula time limit per test 2 seconds memory limit per test 256 megabytes input stand ...

  3. 卢卡斯定理&扩展卢卡斯定理

    卢卡斯定理 求\(C_m^n~mod~p\) 设\(m={a_0}^{p_0}+{a_1}^{p_1}+\cdots+{a_k}^{p_k},n={b_0}^{p_0}+{b_1}^{p_1}+\cd ...

  4. 【learning】 扩展lucas定理

    首先说下啥是lucas定理: $\binom n m \equiv \binom {n\%P} {m\%P} \times \binom{n/P}{m/P} \pmod P$ 借助这个定理,求$\bi ...

  5. BZOJ - 2142 礼物 (扩展Lucas定理)

    扩展Lucas定理模板题(貌似这玩意也只能出模板题了吧~~本菜鸡见识鄙薄,有待指正) 原理: https://blog.csdn.net/hqddm1253679098/article/details ...

  6. [bzoj2142]礼物(扩展lucas定理+中国剩余定理)

    题意:n件礼物,送给m个人,每人的礼物数确定,求方案数. 解题关键:由于模数不是质数,所以由唯一分解定理, $\bmod  = p_1^{{k_1}}p_2^{{k_2}}......p_s^{{k_ ...

  7. Ceizenpok’s formula Gym - 100633J 扩展Lucas定理 + 中国剩余定理

    http://codeforces.com/gym/100633/problem/J 其实这个解法不难学的,不需要太多的数学.但是证明的话,我可能给不了严格的证明.可以看看这篇文章 http://ww ...

  8. bzoj2142 礼物——扩展卢卡斯定理

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2142 前几天学了扩展卢卡斯定理,今天来磕模板! 这道题式子挺好推的(连我都自己推出来了) , ...

  9. 【知识总结】扩展卢卡斯定理(exLucas)

    扩展卢卡斯定理用于求如下式子(其中\(p\)不一定是质数): \[C_n^m\ mod\ p\] 我们将这个问题由总体到局部地分为三个层次解决. 层次一:原问题 首先对\(p\)进行质因数分解: \[ ...

随机推荐

  1. 自定义制作SpringBoot启动图案

    自定义制作SpringBoot启动图案 一.首先在SpringBoot项目的resources的目录下新建banner.txt文件 二.自定义启动图案 自定义启动图案地址 三.将生成的图形复制粘贴到b ...

  2. CentOS Nginx的安装与部署

    安装工具包 wget.vim和gcc yum install -y wget yum install -y vim-enhanced yum install -y make cmake gcc gcc ...

  3. SoftPool:基于Softmax加权的池化操作 | 2021新文

    SoftPool使用softmax进行加权池化,能够保持特征的表达性并且是可微操作.从性能和准确率来看,SoftPool是目前的常规池化方法的一个不错的替代品   来源:晓飞的算法工程笔记 公众号 论 ...

  4. Vert.X CompositeFuture 用法

    CompositeFuture 是一种特殊的 Future,它可以包装一个 Future 列表,从而让一组异步操作并行执行:然后协调这一组操作的结果,作为 CompositeFuture 的结果.本文 ...

  5. 【ConcurrentHashMap】浅析ConcurrentHashMap的构造方法及put方法(JDK1.7)

    目录 引言 代码讲解 构造方法 put方法 ensureSegment Segment.put 引言 ConcurrentHashMap的数据结构如下. 和HashMap的最大区别在于多了一层Segm ...

  6. C++基础-6-继承

    6. 继承 1 #include<iostream> 2 using namespace std; 3 4 5 class Base { 6 public: 7 Base() { 8 m_ ...

  7. 2.1 安装Linux系统对硬件有什么要求?

    很多初学者在安装 Linux 系统时,都对自己的电脑配置存在质疑,担心其是否能够满足安装 Linux 的要求.本节就从 CPU.内存.硬盘.显卡等这些方面,详细介绍一下安装 Linux 系统的最低配置 ...

  8. 1 Mybatis动态SQL

    Mybatis动态SQL 1. 注解开发 ​ 我们也可以使用注解的形式来进行开发,用注解来替换掉xml. 使用注解来映射简单语句会使代码显得更加简洁,但对于稍微复杂一点的语句,Java 注解不仅力不从 ...

  9. 第一个MVC程序

    配置版 添加web的支持! 确定导入了SpringMVC 的依赖! 配置web.xml , 注册DispatcherServlet <?xml version="1.0" e ...

  10. Mockito+Junit5单元测试

    参考: https://segmentfault.com/a/1190000006746409 https://waylau.com/mockito-quick-start/ 1.引入依赖 下面这个最 ...