题面:
Description
我们的小朋友很喜欢计算机科学,而且尤其喜欢二叉树。
考虑一个含有 n n 个互异正整数的序列。如果一棵带点权的有根二叉树满足其所有顶点的权值都在集合 c1,c2,…,cn c 1 , c 2 , … , c n 中,我们的小朋友就会将其称作神犇的。并且他认为,一棵带点权的树的权值,是其所有顶点权值的总和。
给出一个整数 m m ,你能对于任意的计算出权值为 s s 的神犇二叉树的个数吗?请参照样例以更好的理解什么样的两棵二叉树会被视为不同的。
我们只需要知道答案关于( 7×17×223+1 7 × 17 × 223 + 1 ,一个质数)取模后的值。
Input
第一行有 2 2 个整数。
第二行有 n n 个用空格隔开的互异的整数。
Output
输出 m m 行,每行有一个整数。第行应当含有权值恰为 i i 的神犇二叉树的总数。请输出答案关于( =7×17×223+1 = 7 × 17 × 223 + 1 ,一个质数)取模后的结果。
Sample Input #1
2 3
1 2
Sample Output #1
1
3
9
Sample Input #2
3 10
9 4 3
Sample Output #2
0
0
1
1
0
2
4
2
6
15
Sample Input #3
5 10
13 10 6 4 15
Sample Output #3
0
0
0
1
0
1
0
2
0
5
HINT
对于第一个样例,有9个权值恰好为3的神犇二叉树:
分析:
设
也即 k k 在中的出现次数(在此处只能为 1 1 或)。
设 V(x) V ( x ) 为 v v 的生成函数:
设权值为 i i 的神犇二叉树的个数为,则我们枚举根的权值和左子树的大小,可以得到一个递归式:
当 i=0 i = 0 时,
那么我们发现这是一个三重卷积。我们知道数列的卷积相当于生成函数的乘法,那么我们设 f f 的生成函数为:
则我们可以得到一个关于 F(x) F ( x ) 的一元二次方程(记得要加上 f0=1 f 0 = 1 时的情况):
也即:
那么我们使用二次方程求根公式得到:
那么到底哪个才是真的 F(x) F ( x ) 呢?
- 若 F(x)=1+1−4V(x)−−−−−−−−√2V(x) F ( x ) = 1 + 1 − 4 V ( x ) 2 V ( x ) :
若 x x 趋向于零,则就会趋向于 f0 f 0 的值。那么我们求 F(x) F ( x ) 在 x→0 x → 0 下的极限:
因为 v0=0 v 0 = 0 ,所以当 x→0 x → 0 时 V(x)→0 V ( x ) → 0 。则有上式相当于:
显然,由于当 x→0 x → 0 时有 2x→0 2 x → 0 且 1+1−4x−−−−−√→2 1 + 1 − 4 x → 2 ,则有:
舍去。
- 若 F(x)=1−1−4V(x)−−−−−−−−√2V(x) F ( x ) = 1 − 1 − 4 V ( x ) 2 V ( x ) :
同理,有:
我们发现当 x→0 x → 0 时 1−1−4x−−−−−√→0 1 − 1 − 4 x → 0 且 2x→0 2 x → 0 ,则我们应用洛必达法则。分子求导可得:
分母求导可得:
则有:
符合 f0=1 f 0 = 1 。
综上,有
为了方便计算,我们构造一个平方差,上下同乘 1+1−4V(x)−−−−−−−−√ 1 + 1 − 4 V ( x ) :
那么我们多项式求逆+多项式求倒解决这道题。
详见https://blog.youkuaiyun.com/ez_tjy/article/details/80213166
代码:
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
typedef long long ll;
const ll p=998244353,g=3;
int nn,n,m,r[262145];
ll inv[262146],c[262145],gn[2][262145],ans;
inline ll pow(ll a,int b){
ll ans=1;
while(b){
if(b&1)ans=ans*a%p;
a=a*a%p;
b>>=1;
}
return ans;
}
inline ll add(ll a,ll b){return a+b>p?a+b-p:a+b;}
inline ll cut(ll a,ll b){return a-b<0?a-b+p:a-b;}
void init(){
for(n=1;n<=m;n<<=1);
nn=n;
gn[0][0]=gn[1][0]=1;
gn[0][1]=pow(g,(p-1)/(n<<1));
gn[1][1]=pow(gn[0][1],p-2);
for(int i=2;i<(n<<1);i++){gn[0][i]=gn[0][i-1]*gn[0][1]%p;gn[1][i]=gn[1][i-1]*gn[1][1]%p;}
inv[1]=1;
for(int i=2;i<=(n<<1);i++)inv[i]=inv[p%i]*(p-p/i)%p;
}
void NTT(ll c[],int n,int tp=1){
for(int i=0;i<n;i++){
r[i]=(r[i>>1]>>1)|((i&1)*(n>>1));
if(i<r[i])swap(c[i],c[r[i]]);
}
for(int i=1;i<n;i<<=1){
for(int j=0;j<n;j+=(i<<1)){
for(int k=0;k<i;k++){
ll x=c[j+k],y=gn[tp!=1][nn/i*k]*c[j+k+i]%p;
c[j+k]=add(x,y);
c[j+k+i]=cut(x,y);
}
}
}
}
void INTT(ll c[],int n){
NTT(c,n,-1);
for(int i=0;i<n;i++)c[i]=c[i]*inv[n]%p;
}
void inverse(ll c[],int n=n){
static ll t[262145],tma[262145];
t[0]=pow(c[0],p-2);
for(int k=2;k<=n;k<<=1){
for(int i=0;i<(k<<1);i++)tma[i]=(i<k?c[i]:0);
for(int i=(k>>1);i<(k<<1);i++)t[i]=0;
NTT(tma,k<<1);
NTT(t,k<<1);
for(int i=0;i<(k<<1);i++)t[i]=cut(add(t[i],t[i]),t[i]*t[i]%p*tma[i]%p);
INTT(t,k<<1);
}
memcpy(c,t,sizeof(ll)*n);
}
void sqrt(ll c[],int n=n){
static ll t[262145],tma[262145],tmb[262145];
t[0]=1;
for(int k=2;k<=n;k<<=1){
for(int i=0;i<k;i++)tma[i]=add(t[i],t[i]);
inverse(tma,k);
for(int i=0;i<(k<<1);i++)tmb[i]=(i<k?c[i]:0);
NTT(tma,k<<1);
NTT(tmb,k<<1);
for(int i=0;i<(k<<1);i++){
ll tmp=tma[i];
tma[i]=t[i];
t[i]=tmp*tmb[i]%p;
}
INTT(t,k<<1);
for(int i=0;i<(k<<1);i++)t[i]=(i<k?add(t[i],tma[i]*inv[2]%p):0);
}
memcpy(c,t,sizeof(ll)*n);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
int x;
scanf("%d",&x);
c[x]=p-4;
}
c[0]=1;
init();
sqrt(c);
c[0]=2;
inverse(c);
for(int i=1;i<=m;i++)printf("%lld\n",add(c[i],c[i]));
}