卡特兰数、Prufer 序列、BSGS 总结

news/2024/9/18 8:54:46/文章来源:https://www.cnblogs.com/laoshan-plus/p/18376030

卡特兰数

定义

给定 \(n\)\(0\)\(n\)\(1\),它们构成一个长度为 \(2n\) 的排列,满足任意前缀中 \(0\) 的个数都不少于 \(1\) 的个数的序列的数量为卡特兰数列。显然 \(H_0=H_1=1\)。(\(H\) 为卡特兰数列)

通项公式:

\[H_n=\frac{\dbinom{2n}{n}}{n+1}\quad (n\ge2,n\in\mathbf N_+) \]

递推公式:

\[\begin{aligned} H_n&=\begin{cases}\displaystyle\sum_{i=1}^nH_{i-1}H_{n-i}&n\ge2,n\in\mathbf N_+\\1&n=0,1\end{cases} \\ &=\frac{H_{n-1}(4n-2)}{n+1} \\ &=\binom{2n}n-\binom{2n}{n-1} \end{aligned} \]

卡特兰数:

\[H=\{1,1,2,5,14,42,132,\dots\} \]

一些结论

  1. 在直角坐标系上,每一步只能向上或向右走,从 \((0,0)\) 走到 \((n,n)\) 并且除两端点为不越过直线 \(y=x\) 的路线数量为 \(2H_{n-1}\)
  2. 同上问题,从 \((0,0)\) 走到 \((n,m)\) 并且不接触直线 \(y=x\) 的路线数量为 \(2\dbinom{2n-2}{n-1}-2\dbinom{2n-2}{n}\)
  3. 同上问题,从 \((0,0)\) 走到 \((n,m)\) 并且不越过直线 \(y=x\) 的路线数量为 \(\dbinom{n+m}{n}-\dbinom{n+m}{n+1}\)
  4. \(n\) 个左括号和 \(n\) 个右括号组成的合法括号序列数量为 \(H_n\)
  5. \(1,2,\dots,n\) 经过一个栈,形成的合法出栈序列的数量为 \(H_n\)
  6. \(n\) 个结点构成的不同二叉树的数量为 \(H_n\)

卡特兰数问题特征

计数问题、排列等,都是卡特兰数问题的特征。

遇到这类可以向卡特兰数方向考虑的问题,可以先手推一遍前几个数,看是否符合卡特兰数列。很多题看着复杂,实际上就是卡特兰数模板题。

有些题不给模数,容易酿成高精度,需要注意。一般高精度的处理方法就是分解质因数,化高精乘除法为加减法,最后只需要一个高精乘低精就可解决。顶多再写一个高精加减低精就可以。笔者在刚打到这里时总是想着码一个万能高精模板出来,但最后发现不仅码量庞大且没有必要,更多时候还是见招拆招,万能模板看似适用,实际上实用性低

Prufer 序列

Prufer 序列是一种将带标号的树用唯一的一个整数序列表示的方法,二者一一对应。不考虑含有一个结点的树。

定义

Prufer 序列可以将一个 \([1,N]\) 结点的树用 \((N-2)\) 个整数表示。Prufer 是这样建立的:每次选择一个编号最小的叶结点并删掉它,然后在序列中记录下它连接到的那个结点。重复 \((N-2)\) 次后就只剩下两个结点,算法结束。

这是一棵 \(7\) 个结点的树的 Prufer 序列构建过程:

对树建立 Prufer 序列

用堆可以做到 \(O(N\log N)\) 的复杂度,但存在线性构造算法。

线性构造的本质就是维护一个指针指向我们将要删除的结点。首先发现,删去一个叶结点,叶结点总数要么不变要么减 \(1\)

于是维护一个指针 \(p\) 指向编号最小的叶节点。每次删掉它之后,如果产生了新的叶节点且编号比 \(p\) 指向的更小,则直接继续删掉,重复这一流程。最后 \(p\) 自增找到下一个编号最小的叶节点。

正确性

\(p\) 已经是当前编号最小的叶节点。如果删 \(p\) 后产生了新的叶节点 \(x\),若 \(x>p\) 不置可否,反正 \(p\) 往后都会扫描到它;但若 \(x<p\),则 \(x\) 成为当前编号最小的叶节点,应优先加入序列。易得 \(p\) 最多遍历每个结点一次,所以复杂度 \(O(N)\)

实现

void solve1() {for (int i = 1; i <= n - 1; i++) cin >> fa[i], deg[fa[i]]++;for (int i = 1, p = 1; i <= n - 2; i++, p++) {while (deg[p]) p++; // 自增找到下一个叶子结点pf[i] = fa[p]; // 加入序列while (i <= n - 2 && --deg[pf[i]] == 0 && pf[i] < p) // 如果产生新叶子结点且编号更小pf[i + 1] = fa[pf[i]], i++;}
}

Prufer 序列重构树

与上面同理的,逆运算。注意因为 Prufer 序列只有 \((N-2)\) 位,而我们要生成的树的父亲序列是 \((N-1)\) 位的,所以要给 Prufer 序列加一位 \(\text{pf}_{N-1}=N\)

void solve2() {for (int i = 1; i <= n - 2; i++) cin >> pf[i], deg[pf[i]]++;pf[n - 1] = n;for (int i = 1, p = 1; i <= n - 2; i++, p++) {while (deg[p]) p++;fa[p] = pf[i];while (i <= n - 2 && --deg[pf[i]] == 0 && pf[i] < p)fa[pf[i]] = pf[i + 1], i++;}
}

Prufer 序列的性质

  1. 构造完 Prufer 序列后原树会剩下两个结点,其中一个一定是编号最大的结点 \(N\)
  2. 每个结点在序列中出现的次数是其度数减 \(\boldsymbol1\)。(没有出现的就是叶节点)

第二点十分重要,是后文的依据。

凯莱公式

完全图 \(\boldsymbol{K_n}\)\(\boldsymbol{n^{n-2}}\) 棵生成树。

用 Prufer 序列证明十分容易。首先,完全图,说明每个结点都有可能出现在 Prufer 序列的每个位置上。最重要的是,从图推树,形态是不限的,也就是说哪怕是 \((n-2)\)\(1\) 也可以,这样推出来的就是一个以 \(1\) 为根结点的 \((n-1)\) 叉树。(带点感性理解,关键是后面有一个易混公式)

一些结论

  1. 一个 \(\boldsymbol n\) 个点 \(\boldsymbol m\) 条边的带标号无向图有 \(\boldsymbol k\) 个连通块,我们希望添加 \(\boldsymbol{(k-1)}\) 条边使图联通。方案数为:

\[n^{k-2}\cdot\prod_{i=1}^ks_i \]

其中 \(s_i\) 表示每个连通块的大小。

  1. 一个无向连通图在给定所有点度情况下的生成树数量为:

\[\frac{(n-2)!}{\prod_{i=1}^n(\textit{deg}_i-1)!} \]

理解 1:由 Prufer 序列性质 2,度数为 \(\textit{deg}_i\) 的点在序列中出现 \((\textit{deg}_i-1)\) 次。给定了每个点的度数,所以这个序列的大小为 \(n-2\) 的可重集是一定的。换句话说,这个序列的每一项都是确定的,情况的不同只是其排列数。每一种排列都对应一种生成树,所以总情况数就是多重组合数,全排列个数除以内部排列个数。

理解 2:尝试解释凯莱公式中的 \(n^{n-2}\) 和本题中的 \((n-2)!\)。因为凯莱公式是由完全图推 Prufer 序列,其序列本身是不确定的,每个位置都有 \(n\) 种情况且可以重复,自然就是 \(n^{n-2}\)。而这里给的信息相当于是确定了 Prufer 序列的所有元素,所以是其内部排列即 \((n-2)!\)

  1. 同上问题,只给定某些点的度数 \(\boldsymbol{\boldsymbol{deg}_i}\),求生成树数量为:

\[\frac{\left(\prod_{t=n-1-s}^{n-2}t\right)(n-k)^{n-2-s}}{\prod_{i=1}^k\left(\textit{deg}_i-1\right)!},s=\sum_{i=1}^k(\textit{deg}_i-1) \]

具体参考这篇题解。

总而言之,Prufer 序列本质上是一个工具,其发明出来的目的就是为了证明凯莱公式,本身意义不大。Prufer 序列给我们的帮助更多的是提供了一种解决关于树的计数问题的思路,而不在于其与树的编解码过程(当然代码也不难背是吧。

BSGS

BSGS 算法,全称 Baby Step, Giant Step 算法,是一种用于求解形如 \(a^x\equiv b\pmod p\) 的高次同余方程的算法,要求 \(a\perp p\),否则可以用 exBSGS 算法。算法的时间复杂度是 \(O(\sqrt p)\)

原理

因为 \(a,p\) 互质,所以可以在模 \(p\) 意义下执行关于 \(a\) 的乘除法运算。

\(x=it-j\),其中 \(t=\left\lceil\sqrt p\right\rceil\)\(0\le j\le t-1\),则方程变为 \(a^{it-j}\equiv b\pmod p\),即 \(a^{it}\equiv b\times a^j\pmod p\)

对于所有的 \(j\in[0,t-1]\),把 \(b\times a^j\) 插入一个 Hash 表,可以用 unordered_map 实现。

枚举 \(i\) 的所有可能取值,即 \(i\in[0,t]\),计算出 \(a^{it}\bmod p\),在 Hash 表中查找是否存在对应的 \(j\),更新答案即可。

实现

int bsgs(int a, int b, int p) {unordered_map<int, int> h;h.clear();b %= p;int t = ceil(sqrt(p));for (int j = 0; j < t; j++) {int val = b * power(a, j, p) % p;h[val] = j;}a = power(a, t, p);if (a == 0) return b == 0 ? 1 : -1;for (int i = 0; i <= t; i++) {int val = power(a, i, p);int j = h.find(val) == h.end() ? -1 : h[val];if (j >= 0 && i * t - j >= 0) return i * t - j;}return -1;
}

代码视为加入了 #define int long long

坑点

\(\boldsymbol{b=1}\),需特判输出 \(\boldsymbol0\)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/786105.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

[Flink] Flink CDC Connactors FAQ

Flink CDC Connactors FAQ 近期遇到 Flink CDC 的问题较多,故基于第1篇参考文献的FAQ文档基础之上,对这些问题做个系统的总结。 MYSQL CDC Q:作业报错 ConnectException: A slave with the same server_uuid/server_id as this slave has connected to the master,怎么办呢?…

少儿编程概述

少儿编程少儿编程 技术参考少儿编程的核心理念是培养孩子思维模式,解决问题逻辑等综合能力,与传统意义上的编程有很大区别。前者培养能力,后者专注技术。国外少儿编程较国内更早开始发展,主要代表有开源平台Scratch以及拥有成熟体系的Tynker。Scratch语言 与 编程猫的Kitte…

《数据资产管理核心技术与应用》读书笔记-第五章:数据服务(一)

《数据资产管理核心技术与应用》是清华大学出版社出版的一本图书,全书共分10章,第1章主要让读者认识数据资产,了解数据资产相关的基础概念,以及数据资产的发展情况。第2~8章主要介绍大数据时代数据资产管理所涉及的核心技术,内容包括元数据的采集与存储、数据血缘、数据质…

【HW系列+技战法】搞定通信加密,力防数据泄露

一、 通信加密流程二、密钥协商算法三、密钥生成四、 密钥交换五、密钥存储原创 菜鸟学信安移动互联网主流的网络通信方式面临诸多风险: 算法破解、协议破解、中间人攻击…… 攻击者利用多种攻击方式,不断对移动应用发起攻击。 在移动应用未做有效保护措施的情况下,如果加密…

基础组件:表单

实际业务中,在正式向服务器提交数据前,都会对各个输入框数据进行合法性校验,但是对每一个TextField都分别进行校验将会是一件很麻烦的事。还有,如果用户想清除一组TextField的内容,除了一个一个清除有没有什么更好的办法呢?为此,Flutter提供了一个Form 组件,它可以对输…

ctfhub-rce-部分做题记录

命令注入 检查网页显示内容,可以直接看到源代码。大致意思是:检查用户输入的 GET 请求,判断用户是否输入了 ip 信息。如果输入了 ip 信息,则使用用户输入的这个 ip 数据执行一个 shell 命令 "ping -c 4" 。 输入 127.0.0.1;cat 104211044913917.php 没回显,查看…

linux 安装nginx

1.nginx 官网下载nginx 包 (选择稳定版) https://nginx.org/en/download.html 2.上传到linux服务器,解压 tar -zxvf nginx-1.26.2.tar.gz 3.cd到 nginx-1.26.2 , 执行 ./configure 4. 执行 make 5 .执行 make install 7.搜索 nginx 所安装的目录 , whereis …

Android Qcom USB Driver学习(八)

因为要看usb charging的问题,所以需要补充一下battery的相关知识,算是入门吧 BAT SCH(1)VBATT_VSNS_P (2)BAT_THERM (3)I2C_SDA (4)I2C_SCL (5)VBATT_VSNS_M sbl1_hw_pre_ddr_init: (1)pm_device_init (2)pm_driver_init (3) pm_sbl_chg_init (1) pm_device_init没有研究过,…

JuiceFS 在多云架构中加速大模型推理

在大模型的开发与应用中,数据预处理、模型开发、训练和推理构成四个关键环节。本文将重点探讨推理环节。在之前的博客中,社区用户 BentoML 和贝壳的案例提到了使用 JuiceFS 社区版来提高模型加载的效率。本文将结合我们的实际经验,详细介绍企业版在此场景下的优势。 下图是一…

基础组件:ICON

Flutter 中,可以像 Web 开发一样使用 iconfont,iconfont 即“字体图标”,它是将图标做成字体文件,然后通过指定不同的字符而显示不同的图片。在字体文件中,每一个字符都对应一个位码,而每一个位码对应一个显示字形,不同的字体就是指字形不同,即字符对应的字形是不同的。…

Docker部署Nginx,无法访问的解决办法

最近用阿里云的服务器部署了一下Nginx,发现无法通过外网访问,排除掉防火墙和端口映射的问题,最终在阿里云官方发现解决办法,docker0网桥的网段与内网eth0网段冲突,可能导致Nginx无法访问,修改Docker的网段后正常访问. 1.运行以下命令,查看docker0和eth0网段是否冲突 route …

一起单测引起的项目加载失败惨案

一、前言最近在开发一个功能模块时,在功能自测阶段,通过使用单测测试功能的完整性,在测试单测联通性使用到静态方法测试时,发现单测报错,通过查阅解决方案发现需要对Javaassist包进行排包或者升版本处理。通过排包解决掉单测报错,在部署项目时发现频繁报bean注入失败问题…