烷烃计数
前置知识
有机化学,生成函数,多项式,牛顿迭代,群论基础 。
引入
由于不考虑立体异构,问题可以抽象为统计无标号无根树每个点度数
下文将由简到难逐渐解决该问题,讲的稍微细一点因为我是萌新。
约定
为了方便表述,下文规定用
烷基计数
根据有机化学基础,烷基计数可以抽象为统计无标号有根树每个点儿子数
考虑树形
考虑怎么转移出
-
$$\sum_{i=0}^{n-1}\sum_{j=0}^{n-1-i}f_i\times f_j\times f_{n-1-i-j}$$ -
$$4\times\sum_{i=0}^{\lfloor\frac{n-1}2\rfloor}f_i\times f_{n-1-2\times i}$$ -
$$2\times[3\mid(n-1)]\sum_{i=0}^{\frac{n-1}3}f_i$$
背包
考虑用牛顿迭代求
我们就是要求
根据泰勒公式有
我们带入
于是就可以进行牛顿迭代分治了,边界
烯烃计数
其实是类似的,还是一个有根问题。把双链看成关键边,那两边是两个烷基,组合去重就是两个儿子的置换,有:
烷烃计数
问题变成一个无根树。把无根树转换成有根树计数:钦定重心为根最后去除子树大小不合法和重心重复。
具体的,先统计以重心为根的方案,即根可以有
当前的答案就是
然后考虑不合法,即子树大小
还要考虑重心重复,不难发现重复当且仅当有两个重心且两个重心子树本质不同,所以再减去:
于是:
这样复杂度是预处理
而会有更毒瘤的问法让你算
我们换一种统计答案的方法,对于任意一个无根树,我们定义无根树的点等价类数为以该树每一个点为根形成的本质不同的有根树的个数,记作
证明:
我们不妨以重心为根(这样最大化保证了等价点与其对应的等价边的对称性),每个点的贡献与其上的边对应。发现如果有两个点属于同一个等价类,那么其上连的边也就属于一个等价类。而重心单独讨论。若
所以答案为:
不难发现两个本质不同的无根树的点等价类是没有交集的,即
时间复杂度
Code
贴一个当多项式板子吧,常数巨大。
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
namespace Modular{
const LL mod=998244353;
inline LL add(const LL a,const LL b){
return a<mod-b?a+b:a-mod+b;
}
inline LL dec(const LL a,const LL b){
return a<b?a-b+mod:a-b;
}
inline LL mul(const LL a,const LL b){
return a*b%mod;
}
inline void Add(LL &a,const LL b){
a=a<mod-b?a+b:a-mod+b;
}
inline void Dec(LL &a,const LL b){
a=a<b?a-b+mod:a-b;
}
inline void Mul(LL &a,const LL b){
a=a*b%mod;
}
inline LL ksm(LL a,LL b){
LL res=1; while(b) (b&1)&&(Mul(res,a),1),Mul(a,a),b>>=1; return res;
}
inline LL Inv(const LL a){
return ksm(a,mod-2);
}
};
using namespace Modular;
namespace Polynome{
struct Poly{
vector<LL> a;
Poly(const int n=0,const LL v=0){ a.resize(n+1),a[n]=v; }
inline LL& operator [] (const int i){ return a[i]; }
inline const LL operator [] (const int i)const{ return a[i]; }
inline Poly rsz(const int n){ (*this).a.resize(n+1);return *this; }
inline int deg()const{ return a.size()-1; }
};
inline void pri(const Poly &A){
for(int i=0;i<=A.deg();i++) cout<<A[i]<<' '; puts("");
}
inline Poly operator + (const Poly &A,const Poly &B){
Poly C(max(A.deg(),B.deg()));
for(int i=0;i<=A.deg();i++) Add(C[i],A[i]);
for(int i=0;i<=B.deg();i++) Add(C[i],B[i]);
return C;
}
inline Poly operator + (const Poly &A,const LL X){
Poly C=A; Add(C[0],X); return C;
}
inline Poly operator - (const Poly &A,const Poly &B){
Poly C(max(A.deg(),B.deg()));
for(int i=0;i<=A.deg();i++) Add(C[i],A[i]);
for(int i=0;i<=B.deg();i++) Dec(C[i],B[i]);
return C;
}
inline Poly operator - (const Poly &A,const LL X){
Poly C=A; Dec(C[0],X); return C;
}
int lim; vector<int> pos;
inline void init(const int n){
lim=1; while(lim<=n) lim<<=1; pos.resize(lim);
for(int i=0;i<lim;i++) pos[i]=(pos[i>>1]>>1)|((i&1)*(lim>>1));
}
inline void NTT(Poly &A,const int type){
for(int i=0;i<lim;i++) if(i<pos[i]) swap(A[i],A[pos[i]]);
for(int b=1;b<lim;b<<=1){ int len=b<<1;
LL w=ksm(type==1?3:(mod+1)/3,(mod-1)/len);
for(int k=0;k<lim;k+=len){ LL wk=1;
for(int i=0;i<b;i++,Mul(wk,w)){
LL A0=A[k+i],A1=mul(wk,A[k+i+b]);
A[k+i]=add(A0,A1),A[k+i+b]=dec(A0,A1);
}
}
}
if(type==-1){ LL inv=Inv(lim); for(int i=0;i<lim;i++) Mul(A[i],inv); }
}
inline Poly operator * (const Poly &A,const Poly &B){
int n=A.deg(),m=B.deg(); init(n+m); Poly A1(lim-1),B1(lim-1);
for(int i=0;i<=n;i++) A1[i]=A[i]; for(int i=0;i<=m;i++) B1[i]=B[i]; NTT(A1,1),NTT(B1,1);
for(int i=0;i<lim;i++) Mul(A1[i],B1[i]); NTT(A1,-1); return A1;
}
inline Poly operator * (const Poly &A,const LL X){
Poly C=A; for(int i=0;i<=C.deg();i++) Mul(C[i],X); return C;
}
inline Poly P_inv(Poly A,const int n){
if(n==1) return Poly(0,Inv(A[0]));
Poly B0=P_inv(A.rsz((n+1)>>1),(n+1)>>1);
return (B0*2-(A*((B0*B0).rsz(n-1))).rsz(n-1)).rsz(n-1);
}
inline Poly P_X2(const Poly &A,const int n){
Poly C(n-1); for(int i=0;i*2<=C.deg();i++) C[i*2]=A[i]; return C;
}
inline Poly P_X3(const Poly &A,const int n){
Poly C(n-1); for(int i=0;i*3<=C.deg();i++) C[i*3]=A[i]; return C;
}
inline Poly P_X4(const Poly &A,const int n){
Poly C(n-1); for(int i=0;i*4<=C.deg();i++) C[i*4]=A[i]; return C;
}
}
using namespace Polynome;
int n; LL inv2,inv6,inv24; Poly X(1);
inline Poly F(const Poly &A,const int n){
return A*(-1)+X*((A*A*A+A*P_X2(A,n)*3+P_X3(A,n)*2)*inv6)+1;
}
inline Poly F_div(const Poly &A,const int n){
return X*((A*A*3+P_X2(A,n)*3)*inv6)-1;
}
inline Poly solve(const int n){
if(n==1) return Poly(0,1); Poly A0=solve((n+1)>>1);
return (A0-F(A0,n)*P_inv(F_div(A0,n).rsz(n-1),n).rsz(n-1)).rsz(n-1);
}
inline void write(const LL x){
if(x>=10) write(x/10); putchar(x%10+48);
}
#define gc getchar()
#define in read()
inline int read(){
int f=1,k=0; char cp=gc; while(cp!='-'&&(cp<'0'||cp>'9')) cp=gc; if(cp=='-') f=-1,cp=gc;
while(cp>='0'&&cp<='9') k=(k<<3)+(k<<1)+cp-48,cp=gc; return f*k;
}
int main(){ inv2=Inv(2),inv6=Inv(6),inv24=Inv(24),X[1]=1;
n=100000;
Poly F=solve(n+1),F2=P_X2(F,n+1),F3=P_X3(F,n+1),F4=P_X4(F,n+1);
Poly P=1+X*(((F*F).rsz(n)*(F*F).rsz(n)).rsz(n)+((F*F).rsz(n)*F2).rsz(n)*6+(F*F3).rsz(n)*8+(F2*F2).rsz(n)*3+F4*6)*inv24;
Poly Q=(((F-1)*(F-1)).rsz(n)+F2-1)*inv2;
Poly S=P_X2(F,n+1);
Poly A=P-Q+S;
for(int i=1;i<=n;i++) write(A[i]),puts("");
return 0;
}