初見安~這裏是傳送門:洛谷P4389 付公主的揹包
題解
30%的數據明顯我們可以暴力跑揹包,但是大點點就不行了。我們考慮多項式。
根據母函數,我們對於每一種物品都可以寫成一個多項式的形式,比如某物品大小爲v,那麼就有多項式:
對於n個物品,我們將他們每個的多項式都乘起來,所求就是最後那個多項式的前n項的係數。
但是這樣做的複雜度是多少?,明顯過不了。所以我們可能還要優化一下公式。
對於第i個物品的多項式爲:
【無窮等比數列求和
所以我們要求的就是:
全是累乘啊。我們把乘法變加法,兩邊同時取對。
現在看起來已經差不多了,那麼我們的問題就是f(i)了。因爲我們的物品並不是真的可以取用無限個的,所以這個式子我們還得繼續展開,在合適的時候回到這個限制上。
因爲有:
所以我們把f(i)帶進去:【這裏是求,直接用代替了。這一步求導+積分可以手推一下,下面是個平方項的,後來約掉了
裏面好像有個,所以我們是時候展開了:【最後這一步積分的還原式手推也會有點點喫力QuQ套求導公式吧。加油
至此,就是了。因爲回到上面,我們括號裏面是求和,所以n個多項式對應的係數相加即可,最後exp回來就是所求了。
【感覺這個題就是在考你數學的導數變換啊……恐怖。】
上代碼——
#include<algorithm>//princes Fu
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<queue>
#define maxn 300005
using namespace std;
typedef long long ll;
const int mod = 998244353;
int read() {
int x = 0, f = 1, ch = getchar();
while(!isdigit(ch)) {if(ch == '-') f = -1; ch = getchar();}
while(isdigit(ch)) x = (x << 1) + (x << 3) + ch - '0', ch = getchar();
return x * f;
}
int len, l, r[maxn];
ll pw(ll a, ll b) {ll res = 1; while(b) {if(b & 1) res = res * a % mod; a = a * a % mod; b >>= 1;} return res;}
void NTT(ll *c, int flag) {
for(int i = 1; i <= len; i++) if(i < r[i]) swap(c[i], c[r[i]]);
for(int mid = 1; mid < len; mid <<= 1) {
ll gn = pw(3, (mod - 1) / (mid << 1));
if(flag == -1) gn = pw(gn, mod - 2);
for(int ls = 0, L = mid << 1; ls < len; ls += L) {
ll g = 1;
for(int k = 0; k < mid; k++, g = g * gn % mod) {
ll x = c[ls + k], y = c[ls + mid + k] * g % mod;
c[ls + k] = (x + y) % mod, c[ls + mid + k] = (x - y + mod) % mod;
}
}
}
if(flag == 1) return;
ll rev = pw(len, mod - 2);
for(int i = 0; i <= len; i++) c[i] = c[i] * rev % mod;
}
ll tmp[maxn];
void get_inv(ll *a, ll *b, int n) {
if(n == 1) {b[0] = pw(a[0], mod - 2); return;};
get_inv(a, b, n + 1 >> 1);
len = 1, l = 0;
while(len <= n + n) len <<= 1, l++;
for(int i = 1; i <= len; i++) r[i] = (r[i >> 1] >> 1) | ((i & 1) << l - 1);
for(int i = 0; i < n; i++) tmp[i] = a[i];
for(int i = n; i <= len; i++) tmp[i] = 0;
NTT(tmp, 1), NTT(b, 1);
for(int i = 0; i <= len; i++) b[i] = b[i] * (1ll * 2 - b[i] * tmp[i] % mod + mod) % mod;
NTT(b, -1); for(int i = n; i <= len; i++) b[i] = 0;
}
void deriv(ll *a, int n) {for(int i = 1; i < n; i++) a[i - 1] = a[i] * i % mod; a[n - 1] = 0;}
void integ(ll *a, int n) {for(int i = n - 1; i > 0; i--) a[i] = a[i - 1] * pw(i, mod - 2) % mod; a[0] = 0;}
void get_ln(ll *a, ll *b, int n) {
get_inv(a, b, n);
for(int i = 0; i < n; i++) tmp[i] = a[i];
for(int i = n; i <= len; i++) tmp[i] = 0;
deriv(tmp, n);
NTT(tmp, 1), NTT(b, 1);
for(int i = 0; i <= len; i++) b[i] = b[i] * tmp[i] % mod;
NTT(b, -1); integ(b, n);
}
ll c[maxn];
void get_exp(ll *a, ll *b, int n) {
if(n == 1) {b[0] = 1; return;}
get_exp(a, b, n + 1 >> 1);
memset(c, 0, sizeof c);
get_ln(b, c, n);
c[0] = (1 - c[0] + a[0] + mod) % mod;
for(int i = 1; i < n; i++) c[i] = (a[i] - c[i] + mod) % mod;
NTT(c, 1), NTT(b, 1);
for(int i = 0; i <= len; i++) b[i] = b[i] * c[i] % mod;
NTT(b, -1);
for(int i = n; i <= len; i++) b[i] = 0;
}//從這裏往上都是爲了求exp的模板操作,可以不看
int n, m, cnt[maxn];
ll f[maxn], g[maxn];
signed main() {
n = read(), m = read();
for(int i = 1, x; i <= n; i++) x = read(), cnt[x]++;//cnt計數,有些物品就可以一起處理了
for(int v = 1; v <= m; v++) if(cnt[v]) {
for(int i = v; i <= m; i += v) f[i] = (f[i] + cnt[v] * pw(i / v, mod - 2));
}//開個桶累加最後推出來的那個公式的內容
get_exp(f, g, m + 1);//exp過去
for(int i = 1; i <= m; i++) printf("%lld\n", g[i]);//就沒了……
return 0;
}
迎評:)
——End——