题目:
给定一个长度为 n n n的序列 c c c, c c c中的数互不相同,要你构造二叉树,树上的每一个点有权值,权值都是序列 c c c中的数。对于 ∀ 1 ≤ i ≤ m \forall 1 \le i \le m ∀1≤i≤m,问权值和为 i i i的二叉树有多少棵。
( 1 ≤ c i , n , m ≤ 1 0 5 ) (1 \le c_i,n,m \le 10^5) (1≤ci,n,m≤105)
题解:
首先考虑一个 d p dp dp,令 g n g_n gn为权值和为 n n n的二叉树的个数,定义数列 { f n } \{f_n\} {fn},若 f i = 1 f_i=1 fi=1,那么 i ∈ c i \in c i∈c,否则, i ∉ c i \notin c i∈/c。
那么可得转移方程
g
n
=
{
1
n
=
0
∑
i
=
1
n
f
i
∑
j
+
k
=
n
−
i
g
j
g
k
n
≠
0
g_n=\begin{cases} 1 & n=0 \\ \displaystyle\sum_{i=1}^nf_i\sum_{j+k=n-i}g_jg_k & n \ne 0 \end{cases}
gn=⎩⎪⎨⎪⎧1i=1∑nfij+k=n−i∑gjgkn=0n=0
其中第二个式子可以写成
g
n
=
∑
i
+
j
+
k
=
n
f
i
g
j
g
k
\displaystyle g_n=\sum_{i+j+k=n}f_ig_jg_k
gn=i+j+k=n∑figjgk,显然是一个卷积式,令
{
f
n
}
\{f_n\}
{fn}的生成函数为
f
(
x
)
f(x)
f(x),
{
g
n
}
\{g_n\}
{gn}的生成函数为
g
(
x
)
g(x)
g(x),那么可以得到
g
(
x
)
=
1
+
∑
i
=
1
n
g
i
x
i
=
1
+
∑
i
=
0
n
∑
i
+
j
+
k
=
n
f
i
g
j
g
k
=
1
+
f
(
x
)
g
2
(
x
)
\displaystyle g(x)=1+\sum_{i=1}^ng_ix^i=1+\sum_{i=0}^n\sum_{i+j+k=n}f_ig_jg_k=1+f(x)g^2(x)
g(x)=1+i=1∑ngixi=1+i=0∑ni+j+k=n∑figjgk=1+f(x)g2(x)。
移项可得 f ( x ) g 2 ( x ) − g ( x ) + 1 = 0 f(x)g^2(x)-g(x)+1=0 f(x)g2(x)−g(x)+1=0,所以 g ( x ) = 1 ± 1 − 4 f ( x ) 2 f ( x ) \displaystyle g(x)=\frac{1 \pm \sqrt{1-4f(x)}}{2f(x)} g(x)=2f(x)1±1−4f(x)。由 lim x → 0 g ( x ) = g ( 0 ) \displaystyle \lim_{x \to 0}g(x)=g(0) x→0limg(x)=g(0),可得 g ( x ) = 1 − 1 − 4 f ( x ) 2 f ( x ) = 2 1 + 1 − 4 f ( x ) \displaystyle g(x)=\frac{1 - \sqrt{1-4f(x)}}{2f(x)}=\frac{2}{1+\sqrt{1-4f(x)}} g(x)=2f(x)1−1−4f(x)=1+1−4f(x)2。多项式开根加求逆即可。
复杂度: O ( n l o g n ) O(nlogn) O(nlogn)
代码:
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<string>
#include<bitset>
#include<sstream>
#include<ctime>
//#include<chrono>
//#include<random>
//#include<unordered_map>
using namespace std;
#define ll long long
#define ls o<<1
#define rs o<<1|1
#define pii pair<int,int>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
#define sz(x) (int)(x).size()
#define all(x) (x).begin(),(x).end()
const double pi=acos(-1.0);
const double eps=1e-6;
// const int mod=998244353;
const int INF=0x3f3f3f3f;
const int maxn=2e5+5;
ll read(){
ll x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
namespace Ploynomial{
const ll mod=998244353,G=3;
int rev[maxn<<1];
ll qpow(ll a,ll p=mod-2){
ll res=1;
while(p){
if(p&1)res=res*a%mod;
a=a*a%mod;
p>>=1;
}
return res;
}
ll invG=qpow(G);
void print(ll *f,int n){
for(int i=0;i<n;i++)printf("%lld ",f[i]);
puts("");
}
void Rev(ll *f,int n){
for(int i=0;i<n;i++){
rev[i]=(rev[i>>1]>>1)|((i&1)?n>>1:0);
if(i<rev[i])swap(f[i],f[rev[i]]);
}
}
ll fix(ll x){
return (x%mod+mod)%mod;
}
void NTT(ll *f,int n,int flag){//1:DFT -1:IDFT
Rev(f,n);
for(int i=1;i<n;i<<=1){
ll wn=qpow(flag>0?G:invG,(mod-1)/(2*i));
for(int j=0;j<n;j+=2*i){
ll w=1;
for(int k=0;k<i;k++){
ll tmp=w*f[j+k+i]%mod;
f[j+k+i]=fix(f[j+k]-tmp);
f[j+k]=fix(f[j+k]+tmp);
w=w*wn%mod;
}
}
}
if(flag<0){
ll invn=qpow(n);
for(int i=0;i<n;i++)
f[i]=f[i]*invn%mod;
}
}
//加法卷积
ll f1[maxn<<1],g1[maxn<<1];
void Mul(ll *f,int n,ll *g,int m,ll *h){
int N;
for(N=1;N<n+m-1;N<<=1);
copy(f,f+n,f1);
fill(f1+n,f1+N,0);
copy(g,g+m,g1);
fill(g1+m,g1+N,0);
NTT(f1,N,1);
NTT(g1,N,1);
for(int i=0;i<N;i++)h[i]=f1[i]*g1[i]%mod;
NTT(h,N,-1);
fill(h+n+m-1,h+N,0);
fill(f1,f1+N,0);
fill(g1,g1+N,0);
}
//多项式求逆 g!=f
ll inv_t[maxn<<1];
void Inv(ll *f,int n,ll *g){
int N;
for(N=1;N<n;N<<=1);
fill(g,g+N,0);
g[0]=qpow(f[0]);
for(int i=2;i<=N;i<<=1){
copy(f,f+i,inv_t);
fill(inv_t+i,inv_t+(i<<1),0);
NTT(g,i<<1,1);
NTT(inv_t,i<<1,1);
for(int j=0;j<(i<<1);j++)
g[j]=fix(2*g[j]-g[j]*g[j]%mod*inv_t[j]%mod);
NTT(g,i<<1,-1);
fill(g+i,g+(i<<1),0);
}
fill(g+n,g+N,0);
fill(inv_t,inv_t+N,0);
}
//求导
int Deriv(ll *f,int n,ll *h){
for(int i=1;i<n;i++){
h[i-1]=f[i]*i%mod;
}
return n-1;
}
//积分
ll inv[maxn];
int Integ(ll *f,int n,ll *h){
inv[1]=1;
for(int i=2;i<=n;i++){
inv[i]=inv[mod%i]*(mod-mod/i)%mod;
}
for(int i=n;i>=1;i--){
h[i]=f[i-1]*inv[i]%mod;
}
h[0]=0;
return n+1;
}
//多项式开根 g!=f
ll sqrt_t[maxn<<1],sqrt_t2[maxn<<1];
void Sqrt(ll *f,int n,ll *g){
int N;
for(N=1;N<n;N<<=1);
fill(g,g+N,0);
g[0]=1;
ll inv2=qpow(2);
for(int i=2;i<=N;i<<=1){
Inv(g,i,sqrt_t);
NTT(sqrt_t,i<<1,1);
NTT(g,i<<1,1);
copy(f,f+i,sqrt_t2);
fill(sqrt_t2+i,sqrt_t2+(i<<1),0);
NTT(sqrt_t2,i<<1,1);
for(int j=0;j<(i<<1);j++){
g[j]=(g[j]*g[j]%mod+sqrt_t2[j])%mod*inv2%mod*sqrt_t[j]%mod;
}
NTT(g,i<<1,-1);
fill(g+i,g+(i<<1),0);
}
fill(g+n,g+N,0);
}
//多项式除法,已知f,g,deg(f)=n,deg(g)=m求q,r,deg(q)=n-m,deg(r)<m,满足f=g*q+r
void Reverse(ll *f,int n){
for(int i=0;i<n/2;i++)swap(f[i],f[n-1-i]);
}
ll div_t[maxn<<1];
void Div(ll *f,int n,ll *g,int m,ll *q,ll *r){
Reverse(f,n);
Reverse(g,m);
Inv(g,n-m+1,div_t);
Mul(f,n,div_t,n-m+1,q);
Reverse(q,n-m+1);
Reverse(g,m);
Reverse(f,n);
Mul(g,m,q,n-m+1,div_t);
for(int i=0;i<m-1;i++)r[i]=fix(f[i]-div_t[i]);
}
//多项式ln
ll ln_t[maxn<<1],ln_t2[maxn<<1];
void Ln(ll *f,int n,ll *h){
Inv(f,n,ln_t);
Deriv(f,n,ln_t2);
Mul(ln_t,n,ln_t2,n-1,h);
fill(h+n-1,h+2*n,0);
// for(int i=n-1;i<2*n-1;i++)h[i]=0;
Integ(h,n-1,h);
fill(ln_t,ln_t+n,0);
fill(ln_t2,ln_t2+n,0);
}
//多项式exp g!=f
ll exp_t[maxn<<1];
void Exp(ll *f,int n,ll *g){
int N;
for(N=1;N<n;N<<=1);
fill(g,g+N,0);
g[0]=1;
for(int i=2;i<=N;i<<=1){
Ln(g,i,exp_t);
for(int j=0;j<i;j++){
exp_t[j]=fix(f[j]-exp_t[j]);
}
exp_t[0]=(exp_t[0]+1)%mod;
Mul(exp_t,i,g,i,g);
fill(g+i,g+2*i,0);
}
fill(g+n,g+N,0);
fill(exp_t,exp_t+N,0);
}
//多项式快速幂,k可以很大,先用mod取模
ll pow_t[maxn<<1];
void Pow(ll *f,int n,ll k,ll *g){
Ln(f,n,pow_t);
for(int i=0;i<n;i++)pow_t[i]=pow_t[i]*k%mod;
Exp(pow_t,n,g);
}
}
using namespace Ploynomial;
int n,m,len=100001;
int c[maxn];
ll f[maxn<<1],invg[maxn<<1],g[maxn<<1];
int main(void){
// freopen("in.txt","r",stdin);
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
scanf("%d",&c[i]);
f[c[i]]=1;
}
for(int i=0;i<len;i++){
f[i]*=-4;
}
f[0]++;
Sqrt(f,len,g);
g[0]++;
Inv(g,len,invg);
for(int i=0;i<len;i++)invg[i]=invg[i]*2%mod;
for(int i=1;i<=m;i++){
printf("%lld\n",invg[i]);
}
return 0;
}