解题思路:
终于卡过去了……
这题和求无标号有根树个数的思路差不多,可以先看这里,因为下面一些公式演算会省略中间过程。
设大小为
n
n
的好图数目为,其中连通的数目为
gn
g
n
注意但
n≥2
n
≥
2
时,不连通的好图和连通的好图一一对应,即
gn=fn/2
g
n
=
f
n
/
2
考虑生成函数
F(x)=∑fixi
F
(
x
)
=
∑
f
i
x
i
,为何方便,我们设
f0=1
f
0
=
1
。
一个好图是由若干个连通好图拼成,即
两边取 In I n 再求导得
再拆回每一项看:
注意
xk−11−xk=xk−1+x2k−1+x3k−1+...
x
k
−
1
1
−
x
k
=
x
k
−
1
+
x
2
k
−
1
+
x
3
k
−
1
+
.
.
.
,所以
[xn]xk−11−xk=[k|n+1]
[
x
n
]
x
k
−
1
1
−
x
k
=
[
k
|
n
+
1
]
,所以
注意 i=0 i = 0 时要移项,且 f0=1,gi=fi/2 f 0 = 1 , g i = f i / 2 ,所以得:
设
si=∑k|ikgk
s
i
=
∑
k
|
i
k
g
k
就可以
O(n2)
O
(
n
2
)
递推了,卡常可过。
也可以分治FFT做到
O(nlog2n)
O
(
n
l
o
g
2
n
)
#include<bits/stdc++.h>
#define ll unsigned long long
using namespace std;
int getint()
{
int i=0,f=1;char c;
for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
if(c=='-')c=getchar(),f=-1;
for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
return i*f;
}
const int N=100005;
int T,n=23333,mod;
inline void add(ll &x,int y){x=x+y>=mod?x+y-mod:x+y;}
ll f[N],g[N],s[N];
ll Pow(ll x,int y)
{
ll res=1;
for(;y;y>>=1,x=x*x%mod)
if(y&1)res=res*x%mod;
return res;
}
int main()
{
//freopen("lx.in","r",stdin);
T=getint(),mod=getint();
f[0]=f[1]=1;for(int i=1;i<=n;i++)s[i]=1;
for(int i=1;i<n;i++)
{
__int128 tmp=0;
for(int j=0;j<=i;j++)tmp+=f[j]*s[i+1-j];tmp%=mod;
g[i+1]=tmp*Pow(i+1,mod-2)%mod,f[i+1]=g[i+1]*2%mod;
for(int j=i+1;j<=n;j+=i+1)add(s[j],tmp);
}
while(T--)cout<<f[getint()]<<'\n';
return 0;
}