「学习笔记」无标号生成树计数总结
感觉关于树/图计数是一门博大精深的学问,不知道这辈子有没有搞到足够明白的机会了啊QwQ
一、无标号有根树计数:
基本上是这篇的详细版本。
考虑令fnf_nfn表示n个点的无标号有根树数量,其生成函数为F(x)=∑i>0fixiF(x)=\sum_{i>0}f_ix^iF(x)=∑i>0fixi;考虑如何计数fn+1f_{n+1}fn+1,可以这么认为:等价于找n个点的森林,每颗树都是一个子问题;然后新增一个点当这些树的根。
一个直接的想法是:是不是可以枚举树的个数,然后的到:F(x)=x∑i>0Fi(x)F(x)=x\sum_{i>0}F^i(x)F(x)=x∑i>0Fi(x)?然而这显然是错的,因为这样得到的答案中有重复计数,即如果我有两个完全相同的儿子,那么其二者的顺序应当是不计的;
因此考虑大小为k的树的选择情况,然后可能又会认为大小为k的树对fn+1f_{n+1}fn+1那一项的贡献是:∑j≥0ifkxki\sum_{j\geq0}i^{f_{k}}x^{ki}∑j≥0ifkxki,但这还是错的,理由同上。
正确的姿势是,贡献应当是:(∑i≥0zik)fk\left(\sum_{i\geq0}z^{ik}\right)^{f_k}(∑i≥0zik)fk,也就是fkf_kfk个相同的等比数列的乘积。一一对应的关系:假设第jjj个等比数列选择的那一项是ijki_jkijk,那么最终对应的一种方案是,假如我们给这fkf_kfk个大小为k的无标号有根树编号为1到fkf_kfk,那么在考虑对fn+1f_{n+1}fn+1的贡献中,第jjj种选了iji_jij个。
最后我们把所有k的情况乘起来(下面即使k≥n+1k\geq n+1k≥n+1也不要紧):
[xn+1]F(x)=[xn+1]x∏k>0(∑i≥0xik)fk=[xn+1]∏k>0(11−xk)fk=[xn+1]∏k>0(1−xk)−fk[x^{n+1}]F(x)=[x^{n+1}]x\prod_{k>0}\left(\sum_{i\geq0}x^{ik}\right)^{f_k}\\=[x_{n+1}]\prod_{k>0}\left(\frac{1}{1-x^k}\right)^{f_k}=[x_{n+1}]\prod_{k>0}\left(1-x^k\right)^{-f_k}[xn+1]F(x)=[xn+1]xk>0∏(i≥0∑xik)fk=[xn+1]k>0∏(1−xk1)fk=[xn+1]k>0∏(1−xk)−fk
把那个[xn+1][x_{n+1}][xn+1]去掉:
F(x)=x∏k>0(1−xk)−fkF(x)=x\prod_{k>0}\left(1-x^k\right)^{-f_k}F(x)=xk>0∏(1−xk)−fk
显然两边取对数:
lnF(x)=ln(x∏k>0(1−xk)−fk)=lnx+∑k>0ln((1−xk)−fk)=lnx−∑k>0fkln(1−xk)\ln F(x)=\ln\left(x\prod_{k>0}\left(1-x^k\right)^{-f_k}\right)\\=\ln x+\sum_{k>0}\ln \left(\left(1-x^k\right)^{-f_k}\right)\\=\ln x-\sum_{k>0}f_k\ln\left(1-x^k\right)lnF(x)=ln(xk>0∏(1−xk)−fk)=lnx+k>0∑ln((1−xk)−fk)=lnx−k>0∑fkln(1−xk)
然后两边同时求导:
F′(x)F(x)=1x+∑k>0fkkxk−11−xk\frac{F'(x)}{F(x)}=\frac 1x+\sum_{k>0}f_k\frac{kx^{k-1}}{1-x^k}F(x)F′(x)=x1+k>0∑fk1−xkkxk−1
化简这个式子,可以得到:
xF′(x)=F(x)+F(x)∑k>0fkkxk1−xkxF'(x)=F(x)+F(x)\sum_{k>0}f_kk\frac{x^k}{1-x^k}xF′(x)=F(x)+F(x)k>0∑fkk1−xkxk
比较其第n项系数,可知(后面最后一项视为F(x)F(x)F(x)和若干多项式的乘积的和):
nfn=fn+∑i>0fi∑k>0fkk([xn−i]xk1−xk)nf_n=f_n+\sum_{i>0}f_i\sum_{k>0}f_kk\left([x^{n-i}]\frac{x^k}{1-x^k}\right)nfn=fn+i>0∑fik>0∑fkk([xn−i]1−xkxk)
我们知道:xk1−xk=∑i=1n−1xik=∑i>0[k∣i]xi\frac{x^k}{1-x^k}=\sum_{i=1}^{n-1}x^{ik}=\sum_{i>0}[k|i]x^i1−xkxk=∑i=1n−1xik=∑i>0[k∣i]xi
因此:
nfn=fn+∑i=1n−1fi∑k>0fkk[k∣n−i]=fn+∑i=1n−1fi∑k∣n−ifkknf_n=f_n+\sum_{i=1}^{n-1}f_i\sum_{k>0}f_kk[k|n-i]=f_n+\sum_{i=1}^{n-1}f_i\sum_{k|n-i}f_kknfn=fn+i=1∑n−1fik>0∑fkk[k∣n−i]=fn+i=1∑n−1fik∣n−i∑fkk
最后
fn=∑i=1n−1fi∑k∣n−ifkkn−1f_n=\frac{\sum_{i=1}^{n-1}f_i\sum_{k|n-i}f_kk}{n-1}fn=n−1∑i=1n−1fi∑k∣n−ifkk
后面那一项显然可以边算边处理,这样直接算是O(n2)O\left(n^2\right)O(n2)的,可以分治NTT做到O(nlg2n)O\left(n\lg^2n\right)O(nlg2n)。
二、无标号无根树计数
其实做完上面的部分这一部分也就基本完成了:令hnh_nhn表示n个点的无标号无根树,我们用有根树的情况减去当根不为重心的情况:
1)当n为奇数,此时重心只有一个,如果根不是重心,那么其一定有恰好一颗子树,其大小超过⌊n2⌋\left\lfloor\frac{n}{2}\right\rfloor⌊2n⌋,枚举这个子树的大小并且扣去:
hn=fn−∑k=⌊n2⌋+1n−1fkfn−kh_n=f_n-\sum_{k=\left\lfloor\frac{n}{2}\right\rfloor+1}^{n-1}f_kf_{n-k}hn=fn−k=⌊2n⌋+1∑n−1fkfn−k
2)和奇数稍有不同的是,此时有可能有两个重心(只是有可能)。如果根不是重心,还是像奇数一样减去那颗大小最大的,然后如果重心是在边e的两端,那么要从总数减去断开e后两颗树不一样(此时在fnf_nfn中会被计数两次)的情况:
hn=fn−(∑k=n2+1n−1fkfn−k)−(fn22)h_n=f_n-\left(\sum_{k=\frac{n}{2}+1}^{n-1}f_kf_{n-k}\right)-\binom{f_{\frac n2}}2hn=fn−⎝⎛k=2n+1∑n−1fkfn−k⎠⎞−(2f2n)
这样可以在O(n)O(n)O(n)的求出某一项,也可以做一个卷积……
写了个O(n2)O\left(n^2\right)O(n2),去OEISOEISOEIS了一波确实没挂。
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#define lint long long
#define p 998244353
#define inv2 499122177
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define N 5010
using namespace std;
int f[N],g[N],h[N];
inline int fast_pow(int x,int k,int ans=1)
{ for(;k;k>>=1,x=(lint)x*x%p) if(k&1) ans=(lint)ans*x%p;return ans; }
int main()
{int n;scanf("%d",&n),f[1]=h[1]=1;rep(i,1,n) g[i]=1;rep(i,2,n){rep(j,1,i-1) f[i]+=(lint)f[j]*g[i-j]%p,(f[i]>=p?f[i]-=p:0);f[i]=(lint)f[i]*fast_pow(i-1,p-2)%p;int t=(lint)i*f[i]%p;for(int j=i;j<=n;j+=i) g[j]+=t,(g[j]>=p?g[j]-=p:0);}rep(i,2,n){rep(j,i/2+1,i-1) h[i]+=(lint)f[j]*f[i-j]%p,(h[i]>=p?h[i]-=p:0);if(i%2==0) h[i]+=f[i/2]*(f[i/2]-1ll)%p*inv2%p,(h[i]>=p?h[i]-=p:0);h[i]=f[i]-h[i]+p,(h[i]>=p?h[i]-=p:0);}rep(i,1,n) printf("%d ",f[i]);printf("\n");rep(i,1,n) printf("%d ",h[i]);return !printf("\n");
}
「学习笔记」无标号生成树计数总结相关推荐
- 生成函数Euler变换学习笔记(无标号有根树计数)
众所周知,对于有标号计数的指数型生成函数 f(x)f(x)f(x),将其任意地进行无顺序的组合,得到的生成函数是exp(f(x))exp(f(x))exp(f(x)). 而对于无标号计数的这样的组合, ...
- 「学习笔记」多项式的蛇皮操作
文章目录 「学习笔记」多项式的蛇皮操作 前置知识 趋近 自然常数 对数 逆元 导函数 牛顿迭代与泰勒公式 不定积分与定积分 多项式乘法 多项式求逆元 多项式除法/取模 多项式牛顿迭代法 多项式开根 「 ...
- 「学习笔记」黑马面面布局开发
「学习笔记」黑马面面布局开发 黑马面面布局开发 一.目的 1.1 技术方案 1.2 代码规范 1.2 目录规范 二.流程开发 2.1 蓝湖/摹客协作平台 2.2 适配方案 2.3 初始化文件 2.4 ...
- 「学习笔记」移动Web开发之flex布局9
「学习笔记」移动Web开发之flex布局9 一.flex布局体验 1.1 传统布局与flex布局 1.2 初体验 二.flex布局原理 2.1 布局原理 三.flex布局父项常见属性 3.1 常见父项 ...
- 「学习笔记」品优购项目-上(页面公共部分 )
「学习笔记」品优购项目-上 品优购项目-上 目标 品优购项目规划 网站制作流程 品优购项目介绍 品优购项目的学习目的 开发工具以及技术栈 开发工具 技术栈 品优购项目搭建工作 创建的文件夹如下(称为项 ...
- 「学习笔记」移动Web开发之rem适配布局10
「学习笔记」移动Web开发之rem适配布局10 一.rem单位 1.1 rem 单位 二.媒体查询 2.1 什么是媒体查询 2.2 语法规范 2.2.1 mediatype 查询类型 2.2.2 关键 ...
- 「学习笔记」HTML5CSS3提高6(上)
「学习笔记」HTML5&CSS3提高6(上) HTML5新特性 概述 语义化标签 (★★) 多媒体标签 视频标签- video(★★★) 基本使用 兼容写法 video 常用属性 音频标签- ...
- 「学习笔记」ISAP求最大流
ISAP学习笔记 ISAP是OI中求最大流的常用方法之一.相对于Dinic,ISAP的速度提升了很多,但编码复杂度也上升了不少. 约定 采用邻接表存储图,对于每条弧,增加一条容量为0的逆向边. d数组 ...
- 「学习笔记」多项式相关
序 学多项式也有好久了,可是我自己还没怎么认认真真推过柿子,导致啥都不会,然后被吊打. 看来再不回顾一下就不行了啊. 多项式乘法 写了一个好看一点的\(\mathrm{NTT}\)板子,仅供参考. i ...
最新文章
- github pages部署静态网页
- webmin远程命令执行漏洞(cve-2019-15107)深入分析
- python新手入门讲解-这是大多数新手入门之后强烈推荐的python自学入门指南秘笈...
- SGU155(笛卡尔树的构造)
- Python 第五天
- Android SQLite数据库demo。架构组件Room
- 【快速入门ORM框架之Dapper】大牛勿进系列
- 第E题 转换任意进制 (java方法直接解)==输入一个十进制数N,将它转换成R进制数输出
- 代码证年审 年报 附文档 短消息类服务接入代码 电信业务资源综合管理系统用户手册-码号年报 (码号使用单位)
- 美化博客园 添加网易云音乐及生成文章目录
- React高阶组件学习总结
- python | prophet的案例实践:趋势检验、突变点检验等
- python元组元素的提取_Python 元组
- 计算机毕业设计参考文献
- matlab离散系统的频率响应,离散系统的频率响应分析
- ubuntu linux目录绿色高亮问题
- 烽火戏诸侯 《剑来》 最新章节下载阅读,mobi、amz3、epub格式
- 商业智能助力 银行业数据“挖金”
- 制作linux包 u盘安装
- 分布式图数据库JanusGraph-简介