bzoj3625

fft

分治虽然是万能的,但是太慢了

分治是nlog^2n的,太慢了,于是我们用求逆和开根

设f(x)表示答案为x的方案数

c表示物品的生成函数

那么f=f*f*c+1

f*f表示左右儿子的方案数

c表示根的方案数

+1是空树,也就是+上t(x)=1这个生成函数

然后求根公式得出f=2/(1+sqrt(1-4*g))

为什么是+号呢?因为x=1时方案数=1,那么只能是+号

然后就是多项式开根和求逆

具体看picks博客

然后是卡常

所有数组开int

fft蝴蝶操作一定要开int

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int P = 998244353;
ll power(ll x, int t)
{
    ll ret = 1;
    for(; t; t >>= 1, x = x * x % P) if(t & 1) ret = ret * x % P;
    return ret;
}
const int N = (1 << 18) + 2;
int n, k, m;
int rev[N];
ll inv2;
void ntt(int *a, int n, int k, int f)
{
    for(int i = 0; i < n; ++i) if(i < rev[i]) swap(a[i], a[rev[i]]);
    for(int l = 2; l <= n; l <<= 1)
    {
        ll w = power(3, f == 1 ? (P - 1) / l : P - 1 - (P - 1) / l);
        int m = l >> 1;
        for(int i = 0; i < n; i += l)
        {
            ll t = 1;
            for(int k = 0; k < m; ++k, t = t * w % P)
            {
                int x = a[i + k], y = t * a[i + k + m] % P;
                a[i + k] = (x + y) % P;
                a[i + m + k] = (x - y + P) % P;
            }
        }
    }
    if(f == -1)
    {
        ll inv = power(n, P - 2);
        for(int i = 0; i < n; ++i) a[i] = a[i] * inv % P;
    }
}
int a[N], b[N], tmp[N], B[N], c[N];
void poly_inverse(int *a, int *b, int l)
{
    if(l == 1)
    {
        b[0] = power(a[0], P - 2);
        return;
    }
    poly_inverse(a, b, l >> 1);
    int n = 1, k = 0;
    while(n <= l) n <<= 1, ++k;
    for(int i = 0; i < n; ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (k - 1));
    for(int i = 0; i < l; ++i) tmp[i] = a[i];
    for(int i = l; i < n; ++i) tmp[i] = 0;
    ntt(tmp, n, k, 1);
    ntt(b, n, k, 1);
    for(int i = 0; i < n; ++i) b[i] = (ll)b[i] * (2 - (ll)tmp[i] * b[i] % P + P) % P;
    ntt(b, n, k, -1);
    for(int i = l; i < n; ++i) b[i] = 0;
}
void poly_sqrt(int *a, int *b, int l)
{
    if(l == 1)
    {
        b[0] = 1;
        return;
    }
    int n = 1, k = 0;
    while(n <= l) n <<= 1, ++k;
    poly_sqrt(a, b, l >> 1);
    for(int i = 0; i < n; ++i) B[i] = 0;
    poly_inverse(b, B, l);
    for(int i = 0; i < n; ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (k - 1));
    for(int i = 0; i < l; ++i) tmp[i] = a[i];
    for(int i = l; i < n; ++i) tmp[i] = 0, B[i] = 0;
    ntt(B, n, k, 1);
    ntt(tmp, n, k, 1);
    ntt(b, n, k, 1);
    for(int i = 0; i < n; ++i) b[i] = (ll)inv2 * (b[i] + (ll)B[i] * tmp[i] % P) % P;
    ntt(b, n, k, -1);
    for(int i = l; i < n; ++i) b[i] = 0;
}
int main()
{
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; ++i)
    {
        int x;
        scanf("%d", &x);
        c[x] -= 4;
    }
    inv2 = power(2, P - 2);
    int len = 1;
    while(len <= m) len <<= 1;
    ++c[0];
    for(int i = 1; i <= m; ++i) if(c[i] < 0) c[i] += P;
    poly_sqrt(c, b, len);
    ++b[0];
    b[0] %= P;
    poly_inverse(b, a, len);
    for(int i = 1; i <= m; ++i) printf("%d\n", a[i] * 2 % P);
    return 0;
}

时间: 2024-09-20 21:36:58

bzoj3625的相关文章

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

传送门 终于学会多项式开根了哈哈-- 反正题解都烂大街了,我就不写了,直接贴代码算了--(犯懒ing) 1 /************************************************************** 2 Problem: 3625 3 User: _Angel_ 4 Language: C++ 5 Result: Accepted 6 Time:29048 ms 7 Memory:5944 kb 8 ******************************

NTT+多项式求逆+多项式开方(BZOJ3625)

定义多项式h(x)的每一项系数hi,为i在c[1]~c[n]中的出现次数. 定义多项式f(x)的每一项系数fi,为权值为i的方案数. 通过简单的分析我们可以发现:f(x)=2/(sqrt(1-4h(x))+1) 于是我们需要多项式开方和多项式求逆. 多项式求逆: 求B(x),使得A(x)*B(x)=1 (mod x^m) 考虑倍增. 假设我们已知A(x)*B(x)=1 (mod x^m),要求C(x),使得A(x)*C(x)=1 (mod x^(2m)) 简单分析可得C(x)=B(x)*(2-A

[Codeforces438E][bzoj3625] 小朋友和二叉树 [多项式求逆+多项式开根]

题面 传送门 思路 首先,我们把这个输入的点的生成函数搞出来: \(C=\sum_{i=0}^{lim}s_ix^i\) 其中\(lim\)为集合里面出现过的最大的数,\(s_i\)表示大小为\(i\)的数是否出现过 我们再设另外一个函数\(F\),定义\(F_k\)表示总权值为\(k\)的二叉树个数 那么,一个二叉树显然可以通过两个子树(可以权值为0,也就是空子树)和一个节点构成 那么有如下求\(F\)的式子 \(F_0=1\) \(F_k=\sum_{i=0}^k s_i \sum_{j=0

【算法总结】多项式相关

[快速傅里叶变换] [相关资料] <虚数的图解>        <虚数的意义>        <FFT学习笔记> <从多项式乘法到快速傅里叶变换>        <Fast Fourier Transform> <[快速傅里叶变换][FFT][WikiOI][P3132][高精度练习之超大整数乘法]> [模板代码] [FFT] 1 const double pi=acos(-1); 2 struct cpx{double r,i;cp