帶限制的無標號樹計數

前言

我又來學數樹了
最近化學老師教我們手把手寫烷烴同分異構體
然後我又想到了這個遠古老題

Dp

首先我們計有根樹,假設度數限制爲m
Fi,jF_i{,_j}表示i個點,根節點子樹大小爲j的樹的個數
Ai=j=0m1Fi,jAi=\sum_{j=0}^{m-1}F_{i,j}
考慮去掉根節點會得到j個子樹,按子樹大小分組,設第i組的大小爲si,有ki個
我們會有Fi,j=st=j,stkt=i(kt+Ast1kt)F_{i,j}=\sum_{\sum s_t=j,{\sum s_tk_t=i}}\prod{{k_t+A_{s_t}-1}\choose {k_t}}
後面那個組合數的意思大概是擋板,就是將k種子樹分成As組,每組對應一種方案
但是這個東西要怎麼寫呢?
考慮枚舉最大的子樹,用類似揹包的方法來轉移
因爲是從小到大枚舉最大的子樹所以不會記重
複雜度大概是O(n^2m log m)的

Code

loj6269烷基計數

#include <cstdio>
#include <cstring>
#include <algorithm>
#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)表示這個東西的生成函數
把同構看成是兒子之間的置換,根據burnside引理我們有A(x)=1+xA(x)3+3A(x2)A(x)+2A(x3)6A(x)=1+x{A(x)^3+3A(x^2)A(x)+2A(x^3)\over 6}
這個東西可以直接N^2Dp了
也可以用分治FFT在O(n log^2 n)的時間內解決

多組詢問的話有空再填
非常抱歉,這個代碼咕咕咕了

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章