[洛谷 P4389]付公主的背包
付公主有一个大小为\(10^5\)的背包。然你有\(n\)种类型的物品,其中第\(i\)种体积为\(V_i\)(是正整数),数量有\(10^5\)件。然后给出一正整数\(m\),对任意\(i=1\ldots m\)求出包里恰好装了\(i\)体积的物品的方案数,答案对\(998244353\)取模。
\(1\leq n\leq 10^5, m\leq 10^5, V_i\leq m\)。
首先,由于每种物品都有\(10^5\)件,我们可以认为每种物品都是无限件的。
然后很显然答案的生成函数就是:\(F(x)=\prod_{i = 1}^n\frac{1}{1-x^{V_i}}\)。
但这玩意暴力NTT算一定没前途,用分治NTT搞复杂度也不对(因为\(V_i\)的和可能非常大)。然后我们对单个物品的生成函数取一下对数,观察到:
\[\ln(\frac{1}{1 - x^c}) = -\ln(1 - x^c) = \sum_{i = 1}^{+\infty}\frac{x^i}{i}\]
最后一步是麦克劳林展开搞出来的……
然后对数可以把加法拆成乘法,然后\(\ln F(x)\)就是每一种物品的对数加起来,把相同体积的和一块然后就是一个经典的调和数复杂度的东西了……
但是我们只知道了\(\ln F(x)\),我们最后要用的是\(F(x)\),这样再求个指数函数就行啦~
#include <cstdio> #include <cstring> #include <cstdlib> #include <algorithm> #include <functional> #include <utility> typedef long long ll; const ll ha = 998244353LL; ll pow_mod(ll a, ll b) { ll ans = 1, res = a; while(b) { if(1LL & b) ans = ans * res % ha; res = res * res % ha; b >>= 1; } return ans; } ll inv(ll x) { return pow_mod(x, ha - 2LL); } const int maxn = 400050; ll invp[maxn]; void process() { invp[1] = 1; for(int i = 2; i < maxn; i ++) { invp[i] = (ha - (ha / (ll)i)); invp[i] = invp[i] * invp[ha % (ll)i] % ha; } } int flip(int bi, int x) { int ans = 0; for(int j = 0; j < bi; j ++) { if((1 << j) & x) { ans |= (1 << (bi - j - 1)); } } return ans; } void NTT(ll *A, int bi, bool flag = false) { int n = 1 << bi; for(int i = 0; i < n; i ++) { int v = flip(bi, i); if(v < i) std::swap(A[v], A[i]); } for(int L = 1; L < n; L <<= 1) { ll xi_n = pow_mod(3LL, (ha - 1LL) / (ll(L << 1))); if(flag) xi_n = inv(xi_n); for(int i = 0; i < n; i += (L << 1)) { ll xi = 1LL; for(int j = i; j < i + L; j ++) { ll x = A[j], y = A[j + L]; ll tmp = y * xi % ha; A[j] = (x + tmp) % ha; A[j + L] = (x - tmp + ha) % ha; xi = xi * xi_n % ha; } } } if(flag) { ll inv_n = inv(n); for(int i = 0; i < n; i ++) { A[i] = A[i] * inv_n % ha; } } } void poly_inv(int mod, ll *B, ll *BB) { if(mod == 1) { BB[0] = inv(B[0]); } else { poly_inv((mod + 1) >> 1, B, BB); int bi = 0, sz = 1; while(sz <= ((mod * 2) + 1)) { bi ++; sz <<= 1; } static ll tmp[maxn]; std::copy(B, B + mod, tmp); std::fill(tmp + mod, tmp + sz, 0LL); NTT(tmp, bi); NTT(BB, bi); for(int i = 0; i < sz; i ++) { tmp[i] = (tmp[i] * BB[i]) % ha; tmp[i] = (tmp[i] * (ha - 1LL)) % ha; tmp[i] = (tmp[i] + 2LL) % ha; tmp[i] = (tmp[i] * BB[i]) % ha; } NTT(tmp, bi, true); std::copy(tmp, tmp + mod, BB); std::fill(BB + mod, BB + sz, 0LL); } } void poly_ln(int mod, ll *B, ll *BB) { ll *tmp = (ll*)calloc(maxn, sizeof(ll)); poly_inv(mod, B, BB); for(int i = 0; i < mod - 1; i ++) tmp[i] = B[i + 1] * (ll(i + 1)) % ha; int bi = 0, sz = 1; while(sz <= ((mod * 2) + 1)) { bi ++; sz <<= 1; } NTT(tmp, bi); NTT(BB, bi); for(int i = 0; i < sz; i ++) BB[i] = BB[i] * tmp[i] % ha; NTT(BB, bi, true); for(int i = mod - 1; i > 0; i --) BB[i] = BB[i - 1] * invp[i] % ha; BB[0] = 0; std::fill(BB + mod, BB + sz, 0LL); free(tmp); } void poly_exp(int mod, ll *B, ll *BB) { if(mod == 1) { BB[0] = 1; } else { poly_exp((mod + 1) >> 1, B, BB); int bi = 0, sz = 1; while(sz <= ((mod * 2) + 1)) { bi ++; sz <<= 1; } std::fill(BB + mod, BB + sz, 0LL); ll *tmp = (ll*)calloc(maxn, sizeof(ll)); poly_ln(mod, BB, tmp); for(int i = 0; i < mod; i ++) { tmp[i] = (B[i] - tmp[i] + ha) % ha; } tmp[0] = (tmp[0] + 1LL) % ha; NTT(tmp, bi); NTT(BB, bi); for(int i = 0; i < sz; i ++) BB[i] = tmp[i] * BB[i] % ha; NTT(BB, bi, true); std::fill(BB + mod, BB + sz, 0LL); free(tmp); } } ll A[maxn], B[maxn]; int cnt[maxn]; int main() { process(); int n, m; scanf("%d%d", &n, &m); for(int i = 1; i <= n; i ++) { int x; scanf("%d", &x); cnt[x] ++; } int N = 100000; for(int i = 1; i <= N; i ++) { if(!cnt[i]) continue; for(int j = 1; j * i <= N; j ++) { ll delta = (ll)cnt[i] * invp[j] % ha; A[j * i] = (A[j * i] + delta) % ha; } } poly_exp(N + 1, A, B); for(int i = 1; i <= m; i ++) { printf("%lld\n", B[i]); } return 0; }
Jul 31, 2019 04:51:59 PM
大佬能否和我加一下联系方式~我的QQ是378528378~