无标号树的计数原理(组合计数,背包问题,隔板法,树的重心)

闲话

一个计数问题入门级选手来搞这种东西

最初的动力来自高一化学课有机物(滑稽)。《同步导练》出了个这样的选择题。

一个结构极其庞大的烷烃(二十几个碳原子),求它的主链长度。

这不是个求树的直径的裸题么?!OI选手扫两眼就出来了,然而别的同学费劲心思找完了还是错的。

于是第一次在常规课中体验到作为OIer的优越感。。。。。。

又是一节课,芙蓉姐开始要我们画己烷、庚烷的同分异构体?!

这不是等于要求节点数为\(n\),点度数不超过\(4\)的无标号的无根树个数吗?没见过,但还是有一点DP思想,蒟蒻开始手动枚举直径,不一会儿也画出来了。

下课以后问芙蓉姐有没有公式。“这个东西要靠计算机知识来推导啦!”

又一次在常规课中体验到作为OIer的优越感。。。。。。

无标号有根树计数

就是有多少种\(n\)个点,每个点度数限制为\(m\)的不同形态的无根树。比如说求烷基就是度数限制为\(4\)的有根树(那个未配对的键当成根就行了)。

放__debug大佬的博客(戳这里

叉姐过来的时候也聊了聊这个有趣的东西。

先设\(f_{i,j}\)为\(i\)个点,根节点度数为\(j\)的方案数。显然\(\sum\limits_{j=0}^mf_{n,j}\)就是答案。

再设\(a_i=\sum\limits_{j=0}^{m-1}f_{i,j}\)。这是个子树背包转移过程中的状态量,去掉\(f_{i,m}\)是因为在树中只有根能有\(m\)个子节点,而其它点都被父节点占去了一个点度。

引入一个Trick:

如果把树(除根以外)分成若干部分,每个部分都由\(k_s\)个大小为\(s\)的子树构成,那么这一部分的方案数是\(\binom{a_s+k_s-1}{k_s}\)

这个要用隔板法解释。

简要介绍一下隔板法——把\(n\)个物品分成\(m\)组的方案数为\(\binom{n+m-1}{m-1}\)(无序,允许有的组为空)

证明的话,可以把分成\(m\)组看成在物品序列中插入\(m-1\)个隔板,相邻隔板(或隔板与序列末端)间就是一组。也就等于有长度为\(n+m-1\)的序列,钦定其中\(m-1\)个为隔板,剩下\(n\)个就是物品了。

\(k\)个子树中,直接钦定每一个选什么,我们并不能计数。因此要运用逆向思维,把\(k\)个子树分成\(a_s\)组,每一组对应一种形态(仔细想想)。这样方案数就可以轻易地写出来了,为\(\binom{a_s+k_s-1}{a_s-1}\)。然而\(a_x\)很大,所以在计算过程中一般写\(\binom{a_s+k_s-1}{k_s}\)

然后求和可以写成这种很不靠谱的式子

\[f_{i,j}=\sum_{k_1+2k_2+...+jk_j=i}\prod_{s=1}^j\binom{a_s+k_s-1}{k_s}\]

这个样子要怎么统计才好呢?

利用DP中的常用思想,保证转移的有序性,我们可以限制转移中出现的最大子树大小\(mx\),再用背包进行转移。具体来说,先从小到大枚举\(mx\),对于每一个\(i,j\),我们可以枚举大小为\(mx\)的子树个数\(k\),从\(f_{i-k\cdot mx,j-k}\)处转移,方程为

\[f_{i,j}\leftarrow \sum_{k=1}^{\max\{j,\lfloor\frac i{mx}\rfloor\}}f_{i-k\cdot mx,j-k}\binom{a_{mx}+k-1}{k}\]

试想一下,因为对于每一种情况,最大的子树大小及其个数是唯一的,所以我们就做到了不重不漏。

算上阶乘需要的时间,复杂度大概是\(O(n^2m\log m)\)的样子。如果是烷基,\(m\)是常数,复杂度就是\(O(n^2)\)

没有找到题目,然而反正下面无根树计数要用。

无标号无根树计数

惊奇地发现__debug巨佬最初跟我想的一样,枚举直径,转而枚举直径的一半进行转移,然而是个\(O(n^4)\)的垃圾算法(貌似前缀和一下可以优化到\(O(n^3)\)?)

然后就知道了要去对以重心为根的有根树计数。好有道理啊!因为重心在树中(至少在奇数个点的树中)是唯一的。

问题就转化为有根树计数。只有两个地方不一样。一个是为了保证最后的根是重心,我们强制\(mx<\lceil\frac n 2\rceil\)。另一个是由前一个引出的,因为假如\(n\)为偶数,那么可能会有两个重心。这两个重心的子树大小都是\(\frac n 2\),所以沿用上面那个隔板法式子就好了。最终结果的方程为

\[ans=\sum_{j=0}^{m}f_{n,j}+[n\mod2=0]\binom{a_{\frac n 2}+1}2\]

题目

[BZOJ4271]chemistry(可以去这里交)

[Tsinsen1287]数树(点击进入题目

然而都需要写高精度。。。。。。

第二个就是点数为\(\frac{n-2}{m-1}\)度数为\(m\)的无根树计数,可以理解为把叶子节点都去掉

然而\(n=6002\)太丧了,连__debug巨佬的python都要跑10s的样子。我再去给高精度卡常估计也没什么用了。生成函数是什么?置换群是什么?以后再填坑吧!

第一个的代码

#include<iostream>
using namespace std;
//高精度太长了不粘了,可以看蒟蒻以前的blog,也可以套个其它的版子进来
const int N=509,m=4;
bign f[N][N];
inline bign C(RG bign n,RG int m){
    RG bign ret=n;RG int i;
    for(i=m;i>1;--i)ret*=--n;
    for(i=m;i>1;--i)ret/=i;
    return ret;
}
int main(){
    f[1][0]=1;//初值
    RG int n,i,j,k,mx;
    RG bign a,ans;
    cin>>n;
    for(mx=1;mx<(n+1)>>1;++mx){//枚举限制
        for(a=j=0;j<m;++j)
            a+=f[mx][j];//预处理a
        for(i=n;i>mx;--i)//从大到小,防止重复计数
            for(j=1;j<=m;++j)
                for(k=1;k<=j&&mx*k<i;++k)
                    f[i][j]+=f[i-mx*k][j-k]*C(a+k-1,k);
    }
    for(ans=j=0;j<=m;++j)
        ans+=f[n][j];
    if(!(n&1)){//特判双重心
        for(a=j=0;j<m;++j)
            a+=f[n>>1][j];
        ans+=C(++a,2);
    }
    cout<<ans<<endl;
    return 0;
}

原文地址:https://www.cnblogs.com/flashhu/p/9457830.html

时间: 2024-10-23 15:54:52

无标号树的计数原理(组合计数,背包问题,隔板法,树的重心)的相关文章

平衡二叉搜索树(AVL树)的原理及实现源代码(有图文详解和C++、Java实现代码)

一.AVL树(平衡二叉搜索树)是什么? AVL树是根据它的发明者G.M. Adelson-Velsky和E.M. Landis命名的.AVL树本质上还是一棵二叉搜索树,它的特点是: 1.本身首先是一棵二叉搜索树. 2.带有平衡条件:每个非叶子结点的左右子树的高度之差的绝对值(平衡因子)最多为1. 例如: 5             5 / \            /  \ 2   6         2   6 / \    \         / \ 1  4   7       1  4

LG5900 无标号无根树计数

有依赖的辅助多项式分治卷积 \[ g_n=\bigoplus_{i=1}^nf_i\f_n=\sum_{i=1}^{n-1}f_ig_{n-i} \] 已知 \(f_0=g_0=0,f_1=1\),求 \(f_2\sim f_n\).对 \(998244353\) 取模. \(n \leq 10^5\). 题解 这类问题的特点: 只有 \(f_1\sim f_n\) 都求出后,\(g_n\) 才能被算出. 卷积的时候不需要 \(g_n\). 普通的分治卷积策略是: \[ (f_l\sim f_{

[从头学数学] 第195节 计数原理

剧情提要: [机器小伟]在[工程师阿伟]的陪同下进入了[九转金丹]之第五转的修炼. 这次要研究的是[计数原理]. 正剧开始: 星历2016年04月25日 10:22:16, 银河系厄尔斯星球中华帝国江南行省. [工程师阿伟]正在和[机器小伟]一起研究[计数原理]. <span style="font-size:18px;"> if (1) { var r = 20; config.setSector(1,10,1,1.5); config.graphPaper2D(0, 0

组合计数小练

组合数学什么的,最有趣了呢-- [51nod 1251] Fox序列的数量 题意 求满足以下条件的序列数目: 序列长度为 $ n $ ,每个元素都属于 $ [1,m] \cap Z $ : 这个序列单调不降: 这个序列出现次数最多的数是唯一的. 数据范围: $ 1≤n,m≤100000 $ ,答案对 $ 1e9+7 $ 取模 题解 先枚举出现次数最多的数的出现次数 $ k $ ,我们要计算的是 $ x_1+x_2+...+x_{m-1}=n-k, ; x_i≤k-1 $ 的非负整数解数目. 可以

Yue Fei&#39;s Battle(组合计数递推)

//求一个直径为 k 的树有多少种形态,每个点的度不超过 3 // 非常完美的分析,学到了,就是要细细推,并且写的时候要细心 还有除法取模需要用逆元 #include <iostream> #include <stdio.h> #include <string.h> #include <math.h> #include <stdlib.h> using namespace std; #define MOD 1000000007 #define L

bzoj 1004 Cards 组合计数

这道题考察的是组合计数(用Burnside,当然也可以认为是Polya的变形,毕竟Polya是Burnside推导出来的). 这一类问题的本质是计算置换群(A,P)中不动点个数!(所谓不动点,是一个二元组(a,p),a∈A,p∈P ,使得p(a)=a,即a在置换p的作用后还是a). Polya定理其实就是告诉了我们一类问题的不动点数的计算方法. 对于Burnside定理的考察,我见过的有以下几种形式(但归根结底还是计算不动点数): 1.限制a(a∈A)的特点,本题即是如此(限制了各颜色个数,可以

概率论1 计数-排列-组合

作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! 概率 概率论研究随机事件.它源于赌徒的研究.赌博中有许多随机事件,比如投掷一个骰子,是否只凭运气呢? 赌徒逐渐发现随机事件的规律.投掷两个骰子是常见的赌博游戏.如果重复很多次,那么总数为2的次数会比总数7的次数少.这就是赌徒把握到的规律:尽管我无法预知事件的具体结果,但我可以了解每种结果出现的可能性.这是概率论的核心. "概率"到底是什么?这在数学上还有争议."

[ZJOI2010]排列计数 (组合计数/dp)

[ZJOI2010]排列计数 题目描述 称一个1,2,...,N的排列P1,P2...,Pn是Magic的,当且仅当2<=i<=N时,Pi>Pi/2. 计算1,2,...N的排列中有多少是Magic的,答案可能很大,只能输出模P以后的值 输入输出格式 输入格式: 输入文件的第一行包含两个整数 n和p,含义如上所述. 输出格式: 输出文件中仅包含一个整数,表示计算1,2,?, 的排列中, Magic排列的个数模 p的值. 输入输出样例 输入样例#1: 20 23 输出样例#1: 16 说明

【组合数学】计数原理

计数原理     ①抽屉原理               有N个抽屉,N+1个苹果,那么至少有一个抽屉有两个或两个以上的苹果.               有N个抽屉,N-1个苹果,那么至少有一个抽屉没有苹果.       ②加法原理         如果A类物品有a个,B类物品有b个,那么A类物品或B类物品共有a+b个(没有性质相同的情况下)       ③乘法原理         如果A有a中发生方式,B有b中发生方式,那么发生事件A与B有a*b中发生方式.       ④容斥原理 ∪=并