luogu P4389 付公主的背包
https://www.luogu.com.cn/problem/P4389
首先知道对于一个体积为
v
v
v的物品,它的生成函数是
1
1
−
x
v
\frac{1}{1-x^v}
1−xv1
显然答案就是
F
=
∏
i
=
1
n
1
1
−
x
v
i
F= \prod_{i=1}^n\frac{1}{1-x^{v_i}}
F=i=1∏n1−xvi1
两边取对数 ln F = ∑ i = 1 n ln 1 1 − x v i \ln F=\sum_{i=1}^n\ln\frac{1}{1-x^{v_i}} lnF=i=1∑nln1−xvi1
然后我们要知道
ln
1
1
−
x
v
=
∑
k
=
1
1
k
x
v
k
\ln\frac{1}{1-x^v}=\sum_{k=1}\frac{1}{k}x^{vk}
ln1−xv1=k=1∑k1xvk
证明可以直接用复合函数求导再积分大力推
这样答案久很简单了,先把
v
i
v_i
vi去个重,然后再把大小一样的丢到一起算,这样是调和级数的
之后把这个多项式exp一下就可以得到答案的生成函数了
code:
#include<bits/stdc++.h>
#define ll long long
#define mod 998244353
#define N 8000005
using namespace std;
ll qpow(ll x, ll y){
ll ret = 1;
for(; y; y >>= 1, x = x * x % mod) if(y & 1) ret = ret * x % mod;
return ret;
}
int rev[N];
const int G = 3;
const int G_inv = qpow(G, mod - 2);
void ntt(ll *a, int n, int o){
for(int i = 1; i < n; i ++) rev[i] = (rev[i >> 1] >> 1) | ((i&1) * n >> 1);
for(int i = 1; i < n; i ++) if(i < rev[i]) swap(a[i], a[rev[i]]);
for(int len = 2; len <= n; len <<= 1){
ll wn = qpow((o == 1)? G:G_inv, (mod - 1) / len);
for(int j = 0; j < n; j += len){
ll w0 = 1;
for(int k = j; k < j + (len >> 1); k ++, w0 = w0 * wn % mod){
int X = a[k], Y = w0 * a[k + (len >> 1)] % mod;
a[k] = (X + Y) % mod;
a[k + (len >> 1)] = (X - Y + mod) % mod;
}
}
}
int ninv = qpow(n, mod - 2); //printf("*%d %d\n", ninv, n);
if(o == -1)
for(int i = 0; i <= n; i ++) a[i] = a[i] * ninv % mod;
}
ll c[N];
void Inv(ll *a, ll *b, int sz){
if(sz == 0) {b[0] = qpow(a[0], mod - 2); return;}
Inv(a, b, sz / 2);
int len = 1;
for(; len <= sz + sz; len <<= 1);
for(int i = 0; i <= sz; i ++) c[i] = a[i];
for(int i = sz + 1; i <= len; i ++) c[i] = 0;
ntt(c, len, 1), ntt(b, len, 1);
for(int i = 0; i <= len; i ++) b[i] = (b[i] * 2 % mod - b[i] * b[i] % mod * c[i] % mod + mod) % mod;
ntt(b, len, -1);
for(int i = sz + 1; i <= len; i ++) b[i] = 0;
}
void qiudao(ll *a, int sz) {
for(int i = 0; i < sz; i ++) a[i] = a[i + 1] * (i + 1) % mod;
a[sz] = 0;
}
void jifen(ll *a, int sz) {
for(int i = sz; i >= 1; i --) a[i] = a[i - 1] * qpow(i, mod - 2) % mod;
a[0] = 0;
}
ll Ad[N], An[N];
void ln(ll *A, int n) {
for(int i = 0; i <= n; i ++) Ad[i] = A[i];
qiudao(Ad, n);
Inv(A, An, n);
int len = 1;
for(; len <= n + n;) len <<= 1;
ntt(Ad, len, 1), ntt(An, len, 1);
for(int i = 0; i <= len; i ++) Ad[i] = Ad[i] * An[i] % mod;
ntt(Ad, len, -1);
jifen(Ad, n);
for(int i = 0; i <= len; i ++) An[i] = 0;
for(int i = 0; i <= n; i ++) A[i] = Ad[i];
}
ll fln[N];
void exp(ll *a, ll *b, int n) {
if(n == 0) {b[0] = 1; return;}
exp(a, b, n / 2);
for(int i = 0; i <= n; i ++) fln[i] = b[i]; ln(fln, n);
fln[0] = (fln[0] + 1) % mod;
for(int i = 1; i <= n; i ++) fln[i] = (a[i] - fln[i] + mod ) % mod;
int len = 1;
for(; len <= n + n;) len <<= 1;
// for(int i = 0; i <= len; i ++) printf("%lld ", b[i]); printf("\n");
//for(int i = 0; i <= len; i ++) printf("%lld ", fln[i]); printf("\n");
ntt(b, len, 1), ntt(fln, len, 1);
for(int i = 0; i <= len; i ++) b[i] = b[i] * fln[i] % mod;
ntt(b, len, -1);
//for(int i = 0; i <= len; i ++) printf("%lld ", b[i]); printf("\n\n");
for(int i = 0; i <= len; i ++) fln[i] = 0;
}
ll a[N], b[N];
int cnt[N], inv[N];
int n, m;
void init(int n) {
inv[0] = inv[1] = 1;
for(int i = 2; i <= n; i ++) inv[i] = 1ll * (mod - mod / i) * inv[mod % i] % mod;
}
int main(){
init(N - 5);
scanf("%d%d", &n, &m);
for(int i = 1, x; i <= n; i ++) {
scanf("%d", &x);
cnt[x] ++;
}
for(int i = 1; i <= m; i ++) if(cnt[i]) {
for(int j = 1; j <= m / i; j ++)
a[i * j] = (a[i * j] + 1ll * cnt[i] * inv[j] % mod) % mod;
}
// for(int i = 0; i <= m; i ++) printf("%lld ", a[i]); printf("\n");
exp(a, b, m);
for(int i = 1; i <= m; i ++) printf("%lld\n", b[i]);
return 0;
}