Skip to main content
1404 words7 min

题解:P6598 烷烃计数

参考资料

题意简述

nn 个碳原子的烷烃的同分异构体数量,对 998244353998244353 取模。

等价于求 nn 个点、每个点度数 4\le 4 的无标号无根树的个数。

烷基计数

烷基(Alkyl)是烷烃去掉一个氢原子得到的基团。从断键处把分子拎起来,失去氢的碳作根,它还能连 33 个碳,其余每个碳有一个父亲、至多 33 个儿子。于是烷基就是每个节点至多 33 个儿子的无标号有根树。

A(x)=n0anxnA(x)=\sum_{n\ge 0}a_nx^nana_nnn 个碳的烷基数。令 a0=1a_0=1,对应空烷基,也就是一个氢原子。

根下挂一个大小 3\le 3 的烷基可重集。A(x)A(x) 已含空元素,所以「恰好挂 33 个」等同于「挂 3\le 3 个非空烷基」。对 S3S_3Burnside 引理(Burnside's Lemma),循环型 (1,1,1)(1,1,1)(1,2)(1,2)(3)(3) 各有 1,3,21,3,2 个置换:

A(x)=1+x6(A3(x)+3A(x)A(x2)+2A(x3))A(x)=1+\frac{x}{6}\left(A^3(x)+3A(x)A(x^2)+2A(x^3)\right)

这是关于 A(x)A(x) 的方程,用 牛顿迭代(Newton's Iteration)解。精度从 xmx^m 倍增到 x2mx^{2m} 时,A(x2)A(x^2)A(x3)A(x^3) 只用到更低次的系数,当成常数。设:

G(A)=A1x6(A3+3A(x2)A+2A(x3))G(A)=A-1-\frac{x}{6}\left(A^3+3A(x^2)A+2A(x^3)\right)

AA 求形式导数:

G(A)=1x2(A2+A(x2))G'(A)=1-\frac{x}{2}\left(A^2+A(x^2)\right)

代入 AAG(A)G(A)A\gets A-\dfrac{G(A)}{G'(A)} 化简:

A(x)1x3(A3(x)A(x3))1x2(A2(x)+A(x2))A(x)\gets\dfrac{1-\dfrac{x}{3}\left(A^3(x)-A(x^3)\right)}{1-\dfrac{x}{2}\left(A^2(x)+A(x^2)\right)}

数论变换(NTT)配上多项式求逆,O(nlogn)O(n\log n) 即可求出 A(x)modxn+1A(x)\bmod x^{n+1}

烷烃计数

烷烃是度数 4\le 4 的无标号无根树。无根树不好直接数,钦定重心为根,转成有根树再容斥。

对一棵无根树数三样东西:

  • 每个点轮流作根,得到的本质不同有根树数,记为点等价类数 pp
  • 每条边作根(两端各挂一棵有根树),得到的本质不同方案数,记为边等价类数 qq
  • 树恰有两个重心、且两重心等价时 s=1s=1,否则 s=0s=0

那么任意一棵无根树都满足 pq+s=1p-q+s=1

s=0s=0 时以重心为根,除重心外每个点唯一对应它的父亲边,两点等价当且仅当父亲边等价,这部分贡献两两抵消,只剩重心自身的 11,即 pq=1p-q=1s=1s=1 时两重心互相等价,pq=1p-q=-1pq+s=1p-q+s=1 照样成立。

对所有无根树求和,记 p,q,s\sum p,\sum q,\sum s 的生成函数为 P(x),Q(x),S(x)P(x),Q(x),S(x)(pq+s)\sum(p-q+s) 就是无根树总数,于是答案是 [xn](P(x)Q(x)+S(x))[x^n]\big(P(x)-Q(x)+S(x)\big)

P(x)P(x) 是点等价类:以某点为根,根的度数 4\le 4,即根下挂一个大小 4\le 4 的烷基可重集。对 S4S_4 用 Burnside 引理,循环型 (14)(1^4)(2,12)(2,1^2)(22)(2^2)(3,1)(3,1)(4)(4) 各有 1,6,3,8,61,6,3,8,6 个置换:

P(x)=x24(A4(x)+6A2(x)A(x2)+3A2(x2)+8A(x)A(x3)+6A(x4))P(x)=\frac{x}{24}\left(A^4(x)+6A^2(x)A(x^2)+3A^2(x^2)+8A(x)A(x^3)+6A(x^4)\right)

Q(x)Q(x) 是边等价类:一条边两端各挂一个烷基,两端无序,端点是碳所以烷基非空。对 S2S_2 用 Burnside 引理:

Q(x)=(A(x)1)2+(A(x2)1)2Q(x)=\frac{(A(x)-1)^2+(A(x^2)-1)}{2}

S(x)S(x) 对应双重心:两端挂同一个烷基:

S(x)=A(x2)S(x)=A(x^2)

答案就是 [xn](P(x)Q(x)+S(x))[x^n]\big(P(x)-Q(x)+S(x)\big)。时间复杂度为 O(nlogn)O(n\log n)

参考代码

2.63 KBcpp
#include <bits/stdc++.h>
using namespace std;

using ll=long long;
const int mod=998244353;
const int g=3;
ll Pow(ll x,ll y)
{
x%=mod;
ll res=1;
while(y)
{
if(y&1)res=res*x%mod;
x=x*x%mod;
y>>=1;
}
return res;
}
const ll inv2=(mod+1)/2;
const ll inv3=Pow(3,mod-2);
const ll inv24=Pow(24,mod-2);
void ntt(vector<ll> &a,int type)
{
int n=a.size();
for(int i=1,j=0;i<n;i++)
{
int bit=n>>1;
for(;j&bit;bit>>=1)j^=bit;
j^=bit;
if(i<j)swap(a[i],a[j]);
}
for(int len=2;len<=n;len<<=1)
{
ll wn=Pow(type==1?g:Pow(g,mod-2),(mod-1)/len);
for(int i=0;i<n;i+=len)
{
ll w=1;
for(int j=0;j<(len>>1);j++)
{
ll u=a[i+j],v=a[i+j+(len>>1)]*w%mod;
a[i+j]=(u+v)%mod;
a[i+j+(len>>1)]=(u-v+mod)%mod;
w=w*wn%mod;
}
}
}
if(type==-1)
{
ll iv=Pow(n,mod-2);
for(int i=0;i<n;i++)a[i]=a[i]*iv%mod;
}
}
vector<ll> mul(vector<ll> a,vector<ll> b,int len)
{
int tot=a.size()+b.size()-1;
int n=1;
while(n<tot)n<<=1;
a.resize(n);
b.resize(n);
ntt(a,1);
ntt(b,1);
for(int i=0;i<n;i++)a[i]=a[i]*b[i]%mod;
ntt(a,-1);
a.resize(min(len,tot));
return a;
}
vector<ll> inv(vector<ll> a,int len)
{
vector<ll> b={Pow(a[0],mod-2)};
for(int k=2;k<(len<<1);k<<=1)
{
vector<ll> c(a.begin(),a.begin()+min((int)a.size(),k));
c.resize(k);
vector<ll> t=mul(mul(b,b,k),c,k);
b.resize(k);
for(int i=0;i<k;i++)b[i]=(2*b[i]%mod-t[i]+mod)%mod;
}
b.resize(len);
return b;
}
vector<ll> comp(const vector<ll> &a,int s,int len)
{
vector<ll> res(len,0);
for(int i=0;i<(int)a.size()&&(ll)i*s<len;i++)res[i*s]=a[i];
return res;
}
vector<ll> alkyl(int len)
{
vector<ll> A={1};
for(int k=2;k<(len<<1);k<<=1)
{
vector<ll> A2=comp(A,2,k),A3=comp(A,3,k);
vector<ll> a2=mul(A,A,k),a3=mul(a2,A,k);
vector<ll> num(k,0),den(k,0);
for(int i=0;i<k;i++)
{
num[i]=(a3[i]-A3[i]+mod)%mod*inv3%mod;
den[i]=(a2[i]+A2[i])%mod*inv2%mod;
}
for(int i=k-1;i>=1;i--){num[i]=num[i-1];den[i]=den[i-1];}
num[0]=den[0]=0;
for(int i=0;i<k;i++){num[i]=(mod-num[i])%mod;den[i]=(mod-den[i])%mod;}
num[0]=(num[0]+1)%mod;
den[0]=(den[0]+1)%mod;
A=mul(num,inv(den,k),k);
}
A.resize(len);
return A;
}
int main()
{
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin>>n;
int m=n+1;
vector<ll> A=alkyl(m);
vector<ll> A2=comp(A,2,m),A3=comp(A,3,m),A4=comp(A,4,m);
vector<ll> a2=mul(A,A,m);
vector<ll> P=mul(a2,a2,m);
vector<ll> p2=mul(a2,A2,m),p3=mul(A2,A2,m),p4=mul(A,A3,m);
for(int i=0;i<m;i++)P[i]=(P[i]+6*p2[i]+3*p3[i]+8*p4[i]+6*A4[i])%mod*inv24%mod;
vector<ll> B=A;
B[0]=(B[0]-1+mod)%mod;
vector<ll> Q=mul(B,B,m);
for(int i=0;i<m;i++)Q[i]=(Q[i]+A2[i])%mod*inv2%mod;
ll ans=((P[n-1]-Q[n]+A2[n])%mod+mod)%mod;
cout<<ans<<'\n';
return 0;
}