版权声明:xgc原创文章,未经允许不得转载。 https://blog.csdn.net/xgc_woker/article/details/82943060
Description
把n个球分成m组,每一组不超过2个,并且不能为空,对于所有小于等于k的分组输出有多少种不同的方案。
Sample Input
3 3
Sample Output
5 5 1
对于暴力的DP,设f[i][j]为前i个球分成j的方案数。
那么
这个东西我们考虑把他变成一个多项式,
f(n)就表示{f[n][0],f[n][1],…,f[n][k]}
倍增去维护他。
对于倍增的过程,我们是这样考虑的。
从高位往低位找二进制位,
每次将两个相同的n合起来。
对于某个位置如果有一,那你就暴力搞。
那么对于两个多项式考虑合并。
这表示不考虑中间两边合起来。
中间放2个,两边合起来。
用NTT加速这个东西。
于是你同时要维护两个东西f(n),f(n-1)
于是再考虑f(2n-1)的转移。。。
就相当于f(n)放一边,f(n-1)放一边,然后再反过来。
但你这样是有重复的。
重复的部分其实就是n那个位置不放两边放n-1
n那个位置放两边放n-1。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const LL mod = 998244353;
int _min(int x, int y) {return x < y ? x : y;}
int _max(int x, int y) {return x > y ? x : y;}
int read() {
int s = 0, f = 1; char ch = getchar();
while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
return s * f;
}
LL h1[150000], h2[150000], A[150000], B[150000], C[150000], D[150000], G[150000];
int k, R[150000];
LL pow_mod(LL a, LL k) {
LL ans = 1;
while(k) {
if(k & 1) (ans *= a) %= mod;
(a *= a) %= mod; k /= 2;
} return ans;
}
void NTT(LL y[], int len, int on) {
for(int i = 0; i < len; i++) if(i < R[i]) swap(y[i], y[R[i]]);
for(int i = 1; i < len; i *= 2) {
LL wn = pow_mod(3, (LL)(mod - 1) / (i * 2)); if(on == -1) wn = pow_mod(wn, mod - 2);
for(int j = 0; j < len; j += i * 2) {
LL w = 1;
for(int k = 0; k < i; k++) {
LL u = y[j + k], v = y[j + k + i] * w % mod;
y[j + k] = (u + v) % mod, y[j + k + i] = (u - v + mod) % mod;
w = w * wn % mod;
}
}
} if(on == -1) {
LL tmp = pow_mod(len, mod - 2);
for(int i = 0; i < len; i++) y[i] = y[i] * tmp % mod;
}
}
void solve(int len) {
memset(C, 0, sizeof(C)), memset(D, 0, sizeof(D));
memcpy(A, h1, sizeof(A)), memcpy(B, h2, sizeof(B));
NTT(A, len, 1), NTT(B, len, 1);
for(int i = 0; i < len; i++) G[i] = A[i] * A[i] % mod;
NTT(G, len, -1);
for(int i = 0; i <= k; i++) C[i] += G[i];
for(int i = 0; i < len; i++) G[i] = B[i] * B[i] % mod;
NTT(G, len, -1);
for(int i = 1; i <= k; i++) (C[i] += G[i - 1]) %= mod;
for(int i = 0; i <= k; i++) (D[i] = (D[i] - G[i] + mod) % mod) %= mod;
for(int i = 1; i <= k; i++) (D[i] = (D[i] - G[i - 1] + mod) % mod) %= mod;
for(int i = 0; i < len; i++) G[i] = A[i] * B[i] % mod;
NTT(G, len, -1);
for(int i = 0; i <= k; i++) (D[i] += G[i] * 2LL % mod) %= mod;
memcpy(h1, C, sizeof(h1)), memcpy(h2, D, sizeof(h2));
}
void vio() {
for(int i = 0; i <= k; ++i) C[i] = h2[i];
for(int i = 0; i <= k; ++i) h2[i] = h1[i];
for(int i = 1; i <= k; ++i) {
h1[i] = (h2[i] + h2[i - 1]) % mod;
h1[i] = (h1[i] + C[i - 1]) % mod;
} h1[0] = 1;
}
int main() {
int n = read(); k = read();
h1[0] = 1;
int len;
for(len = 1; len <= 2 * k + 1; len *= 2);
for(int i = 0; i < len; i++) R[i] = (R[i >> 1] >> 1) | ((i & 1) * (len >> 1));
for(int i = 30; i >= 0; i--) {
solve(len);
if(n >= (1 << i)) vio(), n -= (1 << i);
} for(int i = 1; i <= k; i++) printf("%lld ", h1[i]);
return 0;
}