3625: [Codeforces Round #250]小朋友和二叉树

3625: [Codeforces Round #250]小朋友和二叉树

Time Limit: 40 Sec   Memory Limit: 256 MB
Submit: 408   Solved: 177
[ Submit][ Status][ Discuss]

Description

我们的小朋友很喜欢计算机科学,而且尤其喜欢二叉树。
考虑一个含有n个互异正整数的序列c[1],c[2],...,c[n]。如果一棵带点权的有根二叉树满足其所有顶点的权值都在集合{c[1],c[2],...,c[n]}中,我们的小朋友就会将其称作神犇的。并且他认为,一棵带点权的树的权值,是其所有顶点权值的总和。
给出一个整数m,你能对于任意的s(1<=s<=m)计算出权值为s的神犇二叉树的个数吗?请参照样例以更好的理解什么样的两棵二叉树会被视为不同的。
我们只需要知道答案关于998244353(7*17*2^23+1,一个质数)取模后的值。

Input

第一行有2个整数 n,m(1<=n<=10^5; 1<=m<=10^5)。
第二行有n个用空格隔开的互异的整数 c[1],c[2],...,c[n](1<=c[i]<=10^5)。

Output

输出m行,每行有一个整数。第i行应当含有权值恰为i的神犇二叉树的总数。请输出答案关于998244353(=7*17*2^23+1,一个质数)取模后的结果。

Sample Input

样例一:
2 3
1 2
样例二:
3 10
9 4 3
样例三:
5 10
13 10 6 4 15

Sample Output

样例一:
1
3
9
样例二:
0
0
1
1
0
2
4
2
6
15
样例三:
0
0
0
1
0
1
0
2
0
5

HINT

对于第一个样例,有9个权值恰好为3的神犇二叉树:

Source

[ Submit][ Status][ Discuss]




定义f[i]:权值为i的二叉树的个数,,g[i]:权值为i的无根二叉树个数,,c[i]:是否存在权值为i的节点

枚举根节点的权值,可以得到f[n] = ∑c[i]g[n-i](i=1~n-1)

枚举左右叶子的权值,可以得到g[n] = ∑f[i]f[n-i](i=0~n)

直接的思路是使用CDQ + NTT解决这两个递推式

但是g的计算是要建立在f算好的基础上的,这就造成了某些时刻,f已经算好的长度不够当前层的转移

不过由于g是通过f和自己的卷积构成的,可以特殊判断一下,在后面的计算中将贡献翻倍即可

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#include<vector>
#include<queue>
#include<set>
#include<map>
#include<stack>
#include<bitset>
#include<ext/pb_ds/priority_queue.hpp>
using namespace std;
 
const int maxn = (1 << 18) + 233;
typedef long long LL;
const LL G = 3;
const LL mo = 998244353;
 
int n,m,Max,A[maxn],B[maxn],C[maxn],c[maxn],f[maxn],g[maxn],w[maxn],_w[maxn];
 
inline int Mul(const LL &x,const LL &y) {return x * y % mo;}
inline int Add(const int &x,const int &y) {return x + y < mo ? x + y : x + y - mo;}
inline int Dec(const int &x,const int &y) {return x - y >= 0 ? x - y : x - y + mo;}
 
inline int ksm(int x,int y)
{
    int ret = 1;
    for (; y; y >>= 1)
    {
        if (y & 1) ret = Mul(ret,x);
        x = Mul(x,x);
    }
    return ret;
}
 
inline int getint()
{
    char ch = getchar(); int ret = 0;
    while (ch < '0' || '9' < ch) ch = getchar();
    while ('0' <= ch && ch <= '9')
        ret = ret * 10 + ch - '0',ch = getchar();
    return ret;
}
 
inline void Rader(int *F,int N)
{
    int j = (N >> 1);
    for (int i = 1; i < N - 1; i++)
    {
        if (i < j) swap(F[i],F[j]); int k = (N >> 1);
        while (j >= k) j -= k,k >>= 1; j += k;
    }
}
 
inline void NTT(int *F,int N,int *w,int on)
{
    Rader(F,N);
    for (int k = 2; k <= N; k <<= 1)
        for (int i = 0; i < N; i += k)
        {
            int now = 0;
            for (int j = i; j < i + (k >> 1); j++)
            {
                int u = F[j],v = Mul(w[now],F[j + (k >> 1)]);
                F[j] = Add(u,v); F[j + (k >> 1)] = Dec(u,v); now += n / k;
            }
        }
    if (on == -1)
    {
        int Inv = ksm(N,mo - 2);
        for (int i = 0; i < N; i++) F[i] = Mul(F[i],Inv);
    }
}
 
inline void Solve(int l,int r,int tmp)
{
    if (l > m) return;
    if (l == r) {if (l) g[l] = Add(g[l],Mul(2,f[l])); return;}
    int mid = l + r >> 1,N = r - l + 1; Solve(l,mid,tmp);
     
    for (int i = l; i <= mid; i++) A[i - l] = g[i];
    for (int i = mid + 1; i <= r; i++) A[i - l] = 0;
    for (int i = 0; i < N; i++) B[i] = c[i]; NTT(A,N,w,1); NTT(B,N,w,1);
    for (int i = 0; i < N; i++) C[i] = Mul(A[i],B[i]); NTT(C,N,_w,-1);
    for (int i = mid + 1; i <= r; i++) f[i] = Add(f[i],C[i - l]);
     
    if (Max >= N)
    {
        for (int i = l; i <= mid; i++) A[i - l] = f[i];
        for (int i = mid + 1; i <= r; i++) A[i - l] = 0;
        for (int i = 0; i < N; i++) B[i] = f[i]; NTT(A,N,w,1); NTT(B,N,w,1);
        for (int i = 0; i < N; i++) C[i] = Mul(A[i],B[i]); NTT(C,N,_w,-1);
        for (int i = mid + 1; i <= r; i++) g[i] = Add(g[i],Mul(tmp,C[i - l]));
        Solve(mid + 1,r,tmp);
    }
    else
    {
        for (int i = l; i <= mid; i++) A[i - l] = f[i];
        for (int i = 0; i < (N >> 1); i++) B[i] = f[i];
        for (int i = mid + 1; i <= r; i++) A[i - l] = B[i - l] = 0;
        NTT(A,N,w,1); NTT(B,N,w,1);
        for (int i = 0; i < N; i++) C[i] = Mul(A[i],B[i]); NTT(C,N,_w,-1);
        for (int i = mid + 1; i <= r; i++) g[i] = Add(g[i],Mul(tmp,C[i - l]));
        Solve(mid + 1,r,2);
    }
    if (!l) Max = r + 1;
}
 
int main()
{
    #ifdef DMC
        freopen("DMC.txt","r",stdin);
    #endif
     
    n = getint(); m = getint();
    for (int i = 1; i <= n; i++)
    {
        int x = getint();
        if (x > m) continue; c[x] = 1;
    }
    n = 1; while (n <= m) n <<= 1; //n <<= 1;
    w[0] = 1; w[1] = ksm(G,(mo - 1) / n);
    for (int i = 2; i <= n; i++) w[i] = Mul(w[i - 1],w[1]);
    for (int i = 0; i <= n; i++) _w[i] = w[n - i];
    f[0] = g[0] = 1; Solve(0,n - 1,1);
    for (int i = 1; i <= m; i++) printf("%d\n",f[i]);
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值