前言
我又來學數樹了
最近化學老師教我們手把手寫烷烴同分異構體
然後我又想到了這個遠古老題
Dp
首先我們計有根樹,假設度數限制爲m
設表示i個點,根節點子樹大小爲j的樹的個數
設
考慮去掉根節點會得到j個子樹,按子樹大小分組,設第i組的大小爲si,有ki個
我們會有
後面那個組合數的意思大概是擋板,就是將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,即烷烴計數
考慮設表示這個東西的生成函數
把同構看成是兒子之間的置換,根據burnside引理我們有
這個東西可以直接N^2Dp了
也可以用分治FFT在O(n log^2 n)的時間內解決
多組詢問的話有空再填
非常抱歉,這個代碼咕咕咕了