BZOJ 3625: [Codeforces Round #250]小朋友和二叉树
3625: [Codeforces Round #250]小朋友和二叉树
Time Limit: 40 Sec Memory Limit: 256 MBSubmit: 304 Solved: 130
[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
据说是道生成函数+多项式计算水题,所以机房里只有我这样的蒟蒻才不会做。
设F(x)中的i次方项系数表示权值和为i的二叉树的个数。(这个就是最终答案函数)
设C(x)中的i次方项系数表示权值i是否出现在可选集合内。(这其实就是C集合的bitset表示)
然后得到$F=CF^2+1$,其中C为枚举根节点权值,两个F分别是左右子树,+1代表可能是一棵空树。
化简得到$CF^2-F+1=0$,根据一元二次方程求根公式,这个多项式方程的根就是$\frac{1\pm\sqrt{1-4C}}{2C}$。
然后嘞,就支持一下多项式开方,多项式求逆好了,其中需要用到NTT和两个倍增算法,然而蒟蒻的我并不会,只好向Monster_Yi大佬和LincHpin大爷虚心请教。
1 #include <cstdio> 2 #include <cstring> 3 4 template <class T> 5 inline void read(T &x) 6 { 7 x = 0; 8 char c = getchar(); 9 while (c < 48)c = getchar(); 10 while (c > 47)x = x*10 + c - 48, c = getchar(); 11 } 12 13 template <class T> 14 inline void swap(T &a, T &b) 15 { 16 T c; 17 c = a; 18 a = b; 19 b = c; 20 } 21 22 const int P = 998244353; 23 const int D = 499122177; 24 const int N = 550000; 25 26 inline int pow(int a, int b) 27 { 28 int r = 1; 29 30 while (b) 31 { 32 if (b & 1) 33 r = 1LL * r * a % P; 34 35 b >>= 1, a = 1LL * a * a % P; 36 } 37 38 return r; 39 } 40 41 inline void ntt(int *a, int len, int type) 42 { 43 for (int i = 0, t = 0; i < len; ++i) 44 { 45 if (i > t)swap(a[i], a[t]); 46 for (int j = (len >> 1); (t ^= j) < j; j >>= 1); 47 } 48 49 for (int h = 2; h <= len; h <<= 1) 50 { 51 int wn = pow(5, (P - 1) / h); 52 53 for (int i = 0; i < len; i += h) 54 { 55 int wk = 1; 56 57 for (int j = 0; j < (h >> 1); ++j, wk = 1LL * wk * wn % P) 58 { 59 int t = 1LL * wk * a[i + j + (h >> 1)] % P; 60 61 a[i + j + (h >> 1)] = (a[i + j] - t + P) % P; 62 a[i + j] = (a[i + j] + t) % P; 63 } 64 } 65 } 66 67 if (type == -1) 68 { 69 for (int i = 1; i < (len >> 1); ++i) 70 swap(a[i], a[len - i]); 71 72 int inv = pow(len, P - 2); 73 74 for (int i = 0; i < len; ++i) 75 a[i] = 1LL * a[i] * inv % P; 76 } 77 } 78 79 void inv(int *a, int *b, int len) 80 { 81 if (len == 1) 82 b[0] = pow(a[0], P - 2); 83 else 84 { 85 static int t[N]; 86 87 inv(a, b, len >> 1); 88 89 memcpy(t, a, len * sizeof(int)); 90 memset(t + len, 0, len * sizeof(int)); 91 92 ntt(t, len << 1, 1); 93 ntt(b, len << 1, 1); 94 95 for (int i = 0; i < len << 1; ++i) 96 b[i] = 1LL * b[i] * (2 - 1LL * t[i] * b[i] % P + P) % P; 97 98 ntt(b, len << 1, -1); 99 100 memset(b + len, 0, len * sizeof(int)); 101 } 102 } 103 104 void sqrt(int *a, int *b, int len) 105 { 106 if (len == 1) 107 b[0] = 1; 108 else 109 { 110 static int c[N], d[N]; 111 112 sqrt(a, b, len >> 1); 113 114 memset(d, 0, len * sizeof(int)); 115 memset(d + len, 0, len * sizeof(int)); 116 117 inv(b, d, len); 118 119 memcpy(c, a, len * sizeof(int)); 120 memset(c + len, 0, len * sizeof(int)); 121 122 ntt(c, len << 1, 1); 123 ntt(b, len << 1, 1); 124 ntt(d, len << 1, 1); 125 126 for (int i = 0; i < len << 1; ++i) 127 b[i] = (1LL * c[i] * d[i] % P + b[i]) % P * D % P; 128 129 ntt(b, len << 1, -1); 130 131 memset(b + len, 0, len * sizeof(int)); 132 } 133 } 134 135 int n, m, len; 136 137 int a[N], b[N]; 138 139 signed main(void) 140 { 141 read(n); 142 read(m); 143 144 a[0] = 1; 145 146 for (len = 1; len <= m; len <<= 1); 147 148 for (int i = 1, x; i <= n; ++i) 149 if (read(x), x <= m)a[x] = P - 4; 150 151 sqrt(a, b, len); 152 153 memcpy(a, b, len * sizeof(int)), ++a[0]; 154 memset(b, 0, len * sizeof(int)), inv(a, b, len); 155 memcpy(a, b, len * sizeof(int)); 156 157 for (int i = 1; i <= m; ++i) 158 printf("%d\n", (a[i] << 1) % P); 159 }
@Author: YouSiki