我又来学数树了
最近化学老师教我们手把手写烷烃同分异构体
然后我又想到了这个远古老题
首先我们计有根树,假设度数限制为m
设 F i , j F_i{,_j} Fi,j表示i个点,根节点子树大小为j的树的个数
设 A i = ∑ j = 0 m − 1 F i , j Ai=\sum_{j=0}^{m-1}F_{i,j} Ai=∑j=0m−1Fi,j
考虑去掉根节点会得到j个子树,按子树大小分组,设第i组的大小为si,有ki个
我们会有 F i , j = ∑ ∑ s t = j , ∑ s t k t = i ∏ ( k t + A s t − 1 k t ) F_{i,j}=\sum_{\sum s_t=j,{\sum s_tk_t=i}}\prod{{k_t+A_{s_t}-1}\choose {k_t}} Fi,j=∑∑st=j,∑stkt=i∏(ktkt+Ast−1)
后面那个组合数的意思大概是挡板,就是将k种子树分成As组,每组对应一种方案
但是这个东西要怎么写呢?
考虑枚举最大的子树,用类似背包的方法来转移
因为是从小到大枚举最大的子树所以不会记重
复杂度大概是O(n^2m log m)的
loj6269烷基计数
#include
#include
#include
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
using namespace std;
typedef long long ll;
const int N=5e3+5,Mo=1e9+7;
int pwr(int x,int y) {
int z=1;
for(;y;y>>=1,x=(ll)x*x%Mo)
if (y&1) z=(ll)z*x%Mo;
return z;
}
int n,f[N][5],inv[N],fac[N];
int main() {
scanf("%d",&n);
f[1][0]=1;
fac[0]=1;fo(i,1,n) fac[i]=(ll)fac[i-1]*i%Mo;
inv[n]=pwr(fac[n],Mo-2);fd(i,n-1,0) inv[i]=(ll)inv[i+1]*(i+1)%Mo;
fo(mx,1,n-1) {
int s=0;
fo(j,0,3) (s+=f[mx][j])%=Mo;
fd(i,n,mx+1) {
fo(j,1,4) {
int C=s;
for(int k=1;k<=j&&mx*k<i;k++) {
(f[i][j]+=(ll)f[i-mx*k][j-k]*C%Mo*inv[k]%Mo)%=Mo;
C=(ll)C*(s+k)%Mo;
}
}
}
}
int ans=0;
fo(i,0,3) (ans+=f[n][i])%=Mo;
printf("%d\n",ans);
return 0;
}
这个当m大的时候不太好处理,需要用拆分数的时间枚举划分
举个栗子,当m=4,即烷烃计数
考虑设 A ( x ) A(x) A(x)表示这个东西的生成函数
把同构看成是儿子之间的置换,根据burnside引理我们有 A ( x ) = 1 + x A ( x ) 3 + 3 A ( x 2 ) A ( x ) + 2 A ( x 3 ) 6 A(x)=1+x{A(x)^3+3A(x^2)A(x)+2A(x^3)\over 6} A(x)=1+x6A(x)3+3A(x2)A(x)+2A(x3)
这个东西可以直接N^2Dp了
也可以用分治FFT在O(n log^2 n)的时间内解决
多组询问的话有空再填
非常抱歉,这个代码咕咕咕了