bzoj3625 小朋友和二叉树

这篇博客介绍了如何解决bzoj3625题目,即在给定权值集合中求特定权值的二叉树数量问题。通过建立生成函数和多项式的关系,利用多项式运算找到解题方案,包括构造F(x)和C(x),并通过多项式逆运算求解。
摘要由CSDN通过智能技术生成

题面bzoj3625

题意:一棵带点权的树的权值为所有点的权值和。给定 m m m,求点权在给定集合中的权值为 s ( 1 ≤ s ≤ m ) s(1 \le s \le m) s(1sm)的二叉树的个数。

题解:设 f ( i ) f(i) f(i)为权值为 i i i的二叉树个数, c ( i ) c(i) c(i)为集合中数的生成函数。

f ( n ) = ∑ i = 1 n c ( i ) ∑ j = 0 i f ( j ) ∗ f ( n − i − j ) f(n) = \sum _{i=1} ^n c(i) \sum _{j=0} ^i f(j) * f(n - i - j) f(n)=i=1nc(i)j=0if(j)f(nij)

f ( 0 ) = 1 f(0)=1 f(0)=1

构造生成函数 F ( x ) = ∑ i = 0 ∞ f ( i ) x i F(x) = \sum _{i=0} ^{\infty} f(i) x^i F(x)=i=0f(i)xi C ( x ) = ∑ i = 0 ∞ c ( i ) x i C(x) = \sum _{i=0} ^{\infty} c(i) x^i C(x)=i=0c(i)xi

F ( x ) = C ( x ) ∗ F ( x ) ∗ F ( x ) + 1 F(x) = C(x) * F(x) * F(x) + 1 F(x)=C(x)F(x)F(x)+1

解得 F ( x ) = 1 ± 1 − 4 C ( x ) 2 C ( x ) F(x) = \frac {1 \pm \sqrt{1-4C(x)}} {2C(x)} F(x)=2C(x)1±14C(x)

当取负号时多项式才可逆,上下再同乘共轭根式。

F ( x ) = 1 − 1 − 4 C ( x ) 2 C ( x ) = 1 − ( 1 − 4 C ( x ) ) 2 C ( x ) ( 1 + 1 − 4 C ( x ) ) = 4 C ( x ) 2 C ( x ) ( 1 + 1 − 4 C ( x ) ) = 2 1 + 1 − 4 C ( x ) F(x) = \frac {1 - \sqrt {1 - 4C(x)}} {2C(x)} = \frac {1- (1 - 4C(x))} {2C(x)(1 + \sqrt {1 - 4C(x)})} = \frac {4C(x)} {2C(x)(1+\sqrt {1 - 4C(x)})} = \frac 2 {1 + \sqrt {1 - 4C(x)}} F(x)=2C(x)114C(x) =2C(x)(1+14C(x) )1(14C(x))=2C(x)(1+14C(x) )4C(x)=1+14C(x) 2

套多项式开根,求逆模板即可。

#include <bits/stdc++.h>
using namespace std;
inline int read() {
    int x = 0, f = 1; char c; c = getchar() ;
    while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
    while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar() ;
    return x * f ;
}
typedef long long ll ;
const int maxn = 1e6 + 10 ;
int inv2 ;
namespace Polynomial {
    const int mod = 998244353, g = 3, ginv = 332748118 ;
    inline ll power (ll x, int y) {
        ll res = 1 ;
        while (y) {
            if (y & 1) res = res * x % mod ;
            x = x * x % mod; y >>= 1 ;
        }
        return res ;
    }
    inline void ntt (int a[], int n, int f) {
        for (int i = 0, j = 0; i < n; i ++) {
            if (i > j) swap (a[i], a[j]) ;
            for (int t = n >> 1; (j ^= t) < t; t >>= 1) ;
        }
        static int w[maxn] ;
        for (int i = 0; i <= n; i ++)
            w[i] = power (f ? ginv : g, (mod - 1) / n * i) ;
        for (int i = 2; i <= n; i <<= 1) {
            for (int j = 0; j < n; j += i)
                for (int k = 0; k < (i >> 1); k ++) {
                    int A = a[j + k], B = 1ll * w[n / i * k] * a[j + k + (i >> 1)] % mod ;
                    a[j + k] = (A + B) % mod; a[j + k + (i >> 1)] = (A - B + mod) % mod ;
                }
        }
        if (f) {
            int invn = power (n, mod - 2) ;
            for (int i = 0; i < n; i ++) a[i] = 1ll * a[i] * invn % mod ;
        }
    }
    int a[maxn], b[maxn], A[maxn], B[maxn] ;
    inline void inv (int a[], int b[], int len) {
        if (len == 1) {b[0] = power (a[0], mod - 2); return;}
        inv (a, b, len >> 1) ;
        for (int i = 0; i < len; i ++) A[i] = a[i], B[i] = b[i] ;
        ntt (A, len << 1, 0); ntt (B, len << 1, 0) ;
        for (int i = 0; i < (len << 1); i ++) A[i] = 1ll * A[i] * B[i] % mod * B[i] % mod ;
        ntt (A, len << 1, 1) ;
        for (int i = 0; i < len; i ++) b[i] = (2ll * b[i] % mod - A[i] + mod) % mod ;
        for (int i = 0; i < (len << 1); i ++) A[i] = B[i] = 0 ;
    }
    int C[maxn], D[maxn] ;
    inline void sqrt (int a[], int b[], int len) {
        if (len == 1) {b[0] = a[0]; return;}
        sqrt (a, b, len >> 1) ;
        for (int i = 0; i <= len; i ++) C[i] = a[i] ;
        inv (b, D, len) ;
        ntt (C, len << 1, 0); ntt (D, len << 1, 0) ;
        for (int i = 0; i < (len << 1); i ++) D[i] = 1ll * D[i] * C[i] % mod ;
        ntt (D, len << 1, 1) ;
        for (int i = 0; i < len; i ++) b[i] = 1ll * (D[i] + b[i]) % mod * inv2 % mod ;
        for (int i = 0; i <= (len << 1); i ++) C[i] = D[i] = 0 ;
    }
}
using namespace Polynomial ;
int n, m, c[maxn], d[maxn] ;
int main() {
    inv2 = power (2, mod - 2) ;
    n = read(); m = read() ;
    for (int i = 1; i <= n; i ++) ++ c[read()] ;
    int N ;
    for (N = 1; N <= m; N <<= 1) ;
    for (int i = 0; i < N; i ++) c[i] = (-4ll * c[i] + mod) % mod ;
    c[0] ++; sqrt (c, d, N) ;
    for (int i = 0; i < N; i ++) c[i] = 0 ;
    d[0] = (d[0] + 1) % mod; inv (d, c, N) ;
    for (int i = 1; i <= m; i ++) printf("%d\n", (int)(2ll * c[i] % mod)) ;
    return 0 ;
}

(自己写的多项式开根板子常数巨大)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值