Description
求烷烃 CnH2n+2 C n H 2 n + 2 和烷基 CnH2n+1 C n H 2 n + 1 的同分异构体个数
Input
第一行一整数 T T 表示用例组数,每组用例输入一整数
Output
输出烷烃和烷基的同分异构体个数
Sample Input
9
1
2
3
4
5
6
7
8
9
Sample Output
1 1
1 1
1 2
2 4
3 8
5 17
9 39
18 89
35 211
Solution
先考虑求烷基个数,设其生成函数为
A(x)
A
(
x
)
,也即求有
n
n
个节点、每个节点出度不超过的不同构有根树个数,由
ploya
p
l
o
y
a
,根节点的三棵子树(可以是空树)之间的旋转变换群有
3!=6
3
!
=
6
个,考虑在每种变换群下的保持不变的方案数,对于恒同变换,对三棵子树没有要求,方案为
A3(x)
A
3
(
x
)
;对于一对置换,要求其中两棵子树相同,另一棵子树没有要求,方案为
3A(x)A(x2)
3
A
(
x
)
A
(
x
2
)
;对于三轮换,要求三棵子树相同,方案为
A(x3)
A
(
x
3
)
,故有
由 CDQ C D Q 分治+ NTT N T T 即可求出 A(x) A ( x ) ,其中需要注意的是,当我们考虑区间 [l,mid) [ l , m i d ) 对区间 [mid,r) [ m i d , r ) 的影响时, A(x3) A ( x 3 ) 部分直接统计, A(x)A(x2) A ( x ) A ( x 2 ) 部分用 [Al,...,Amid−1] [ A l , . . . , A m i d − 1 ] 和 A(x2) A ( x 2 ) 做卷积即可,而 A3(x) A 3 ( x ) 部分,注意到只有 a+b+c∈[mid−1,r−1) a + b + c ∈ [ m i d − 1 , r − 1 ) 且 a,b,c a , b , c 中至少有一个介于 [l,mid) [ l , m i d ) 时才会对 Aa+b+c+1 A a + b + c + 1 有贡献 AaAbAc A a A b A c ,若 l=0 l = 0 ,则直接对 A0,...,Amid−1 A 0 , . . . , A m i d − 1 自身做三遍卷积即可,若 l>0 l > 0 ,考虑 a∈[l,mid) a ∈ [ l , m i d ) 的贡献,此时 b,c<r−l b , c < r − l ,而 A0,...,Ar−l A 0 , . . . , A r − l 必然已经求出,故用 Al,...,Amid−1 A l , . . . , A m i d − 1 和 A0,...,Ar−l A 0 , . . . , A r − l 以及 A0,...,Ar−l A 0 , . . . , A r − l 做卷积即可
之后考虑求烷烃个数,设其生成函数为 B(x) B ( x ) ,对于一棵 n n 个节点无根树,设其点等价类有个,边等价类有 d d 个(两个点等价当且仅当以这两个点为根得到的有根树同构,两条边等价当且仅当分别在这两条边中插入一个点,以插入点为根得到的有根树同构),对称边有条(断掉这条边得到的两棵子树同构),由于 e≤1 e ≤ 1 ,分别考虑两种情况
1. e=0 e = 0 ,即没有对称边,此时任选一个重心为根,显然重心这个点成为一个点等价类(否则与其等价的点必然也是重心,两个重心之间必然有边,这条边即为对称边,与 e=0 e = 0 矛盾),之后对于每个点等价类,其中每个元素与其父亲连的边必然也形成一个边等价类,反之同理,故 c−d=1 c − d = 1
2. e=1 e = 1 ,即存在一条对称边,在这条边中间插入一个点作为根,同理除根节点外,每个点等价类对应一个边等价类,故有 c−d=0 c − d = 0
综上有,对任意一棵无根树, c−d+e=1 c − d + e = 1
注意到一个烷烃即为一个
n
n
个节点、每点度数不超过的无根树,对所有不同构的烷烃求和该式有
右端即为所求,分别求出烷烃中 ∑c,∑d,∑e ∑ c , ∑ d , ∑ e 的生成函数 C(x),D(x),E(x) C ( x ) , D ( x ) , E ( x ) 即可
首先考虑 E(x) E ( x ) ,显然 En≠0 E n ≠ 0 时 n n 必然为偶数,把树从对称边切开即得到两棵点数为、每个节点出度不超过 3 3 的有根树,进而有
对于
C(x)
C
(
x
)
,注意到只需任选一个点做根得到的不同构有根树数量即为
Cn
C
n
,同理由
polya
p
o
l
y
a
,四棵子树的旋转变换群有
4!=24
4
!
=
24
种,其中恒同变换对四棵子树没有限制,方案为
A4(x)
A
4
(
x
)
;
3
3
种双对换构成的变换要求两对子树相同,方案为;
6
6
种一对换构成的变换要求一对子树相同,另外两棵子树没有限制,方案为;
8
8
种三轮换构成的变换要求三棵子树相同,另外一棵子树没有限制,方案为;
6
6
种四轮换构成的变换要求四棵子树相同,方案为,故有
而对于 D(x) D ( x ) ,注意到只需任选一条边拆开加点,以新点为根的不同构有根树数量即为 Dn D n ,此时要求两棵子树非空,由 polya p o l y a ,恒同变换对两个子树没有限制,方案为 (A(x)−1)2 ( A ( x ) − 1 ) 2 ,对换要求两棵子树相同,方案为 A(x2)−1 A ( x 2 ) − 1 ,故有
故求出 A(x) A ( x ) 后即可得到 C(x),D(x),E(x) C ( x ) , D ( x ) , E ( x ) ,进而得到 B(x)=C(x)−D(x)+A(x2) B ( x ) = C ( x ) − D ( x ) + A ( x 2 )
Code
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxlen=700000,mod=998244353,g=3;
int mul(int x,int y)
{
ll z=1ll*x*y;
return z-z/mod*mod;
}
int add(int x,int y)
{
x+=y;
if(x>=mod)x-=mod;
return x;
}
int Pow(int x,int y)
{
int ans=1;
while(y)
{
if(y&1)ans=mul(ans,x);
x=mul(x,x);
y>>=1;
}
return ans;
}
int wn[maxlen],pos[maxlen],inv;
void ntt_init(int len)
{
int j=0;
while((1<<j)<len)j++;
j--;
for(int i=0;i<len;i++)
pos[i]=pos[i>>1]>>1|((i&1)<<j);
inv=Pow(len,mod-2);
int x=Pow(g,(mod-1)/len);
wn[0]=1;
for(int i=1;i<=len;i++)wn[i]=mul(x,wn[i-1]);
}
void ntt(int *a,int len,int sta)
{
for(int i=0;i<len;i++)
if(i<pos[i])swap(a[i],a[pos[i]]);
for(int k=1;k<len;k<<=1)
for(int i=0;i<len;i+=k<<1)
{
int t=len/(k<<1);
for(int j=0;j<k;j++)
{
int w=sta==1?wn[t*j]:wn[len-t*j];
int x=a[i+j],y=mul(w,a[i+j+k]);
a[i+j]=add(x,y);a[i+j+k]=add(x,mod-y);
}
}
if(sta==-1)
for(int i=0;i<len;i++)a[i]=mul(a[i],inv);
}
int A[maxlen],A1[maxlen],A2[maxlen],A3[maxlen],A4[maxlen],B[maxlen],C[maxlen],D[maxlen];
int i2,i3,i6,i24;
void Solve(int l,int r)
{
if(l+1==r)return ;
int mid=(l+r)/2,len=(r-l)<<1;
Solve(l,mid);
ntt_init(len);
for(int i=l;i<mid;i++)C[i-l]=A[i];
for(int i=0;i<r-l;i++)
{
A1[i]=A[i];
if(2*i<r-l)A2[2*i]=A[i];
}
for(int i=l;3*i+1<r;i++)
if(3*i+1>=mid)A[3*i+1]=add(A[3*i+1],mul(A[i],i3));
ntt(C,len,1),ntt(A1,len,1),ntt(A2,len,1);
for(int i=0;i<len;i++)D[i]=mul(C[i],A2[i]);
ntt(D,len,-1);
for(int i=mid;i<r;i++)A[i]=add(A[i],mul(D[i-l-1],i2));
for(int i=0;i<len;i++)D[i]=mul(C[i],mul(A1[i],A1[i]));
ntt(D,len,-1);
if(l)
for(int i=mid;i<r;i++)A[i]=add(A[i],mul(D[i-l-1],i2));
else
for(int i=mid;i<r;i++)A[i]=add(A[i],mul(D[i-l-1],i6));
for(int i=0;i<len;i++)C[i]=A1[i]=A2[i]=0;
Solve(mid,r);
}
void Pre(int n)
{
i2=Pow(2,mod-2),i3=Pow(3,mod-2),i6=Pow(6,mod-2),i24=Pow(24,mod-2);
A[0]=1;
Solve(0,n);
for(int i=0;i<n;i++)A1[i]=A2[2*i]=A3[3*i]=A4[4*i]=A[i];
int len=4*n;
ntt_init(len);
ntt(A1,len,1),ntt(A2,len,1),ntt(A3,len,1),ntt(A4,len,1);
for(int i=0;i<len;i++)
{
C[i]=mul(mul(A1[i],A1[i]),mul(A1[i],A1[i]));
C[i]=add(C[i],mul(3,mul(A2[i],A2[i])));
C[i]=add(C[i],mul(6,mul(mul(A1[i],A1[i]),A2[i])));
C[i]=add(C[i],mul(8,mul(A1[i],A3[i])));
C[i]=add(C[i],mul(6,A4[i]));
C[i]=mul(C[i],i24);
}
ntt(C,len,-1);
for(int i=n-1;i;i--)C[i]=C[i-1];
C[0]=0;
for(int i=0;i<len;i++)
{
int x=add(A1[i],mod-1),y=add(A2[i],mod-1);
D[i]=mul(i2,add(mul(x,x),y));
}
ntt(D,len,-1);
for(int i=0;i<n;i++)
{
B[i]=add(C[i],mod-D[i]);
if(i%2==0)B[i]=add(B[i],A[i>>1]);
}
}
int main()
{
Pre(1<<17);
int T,n;
scanf("%d",&T);
while(T--)
{
scanf("%d",&n);
printf("%d %d\n",B[n],A[n]);
}
return 0;
}