洛谷·付公主的揹包

初見安~這裏是傳送門:洛谷P4389 付公主的揹包

題解

30%的數據明顯我們可以暴力跑揹包,但是大點點就不行了。我們考慮多項式。

根據母函數,我們對於每一種物品都可以寫成一個多項式的形式,比如某物品大小爲v,那麼就有多項式:

\small (1+x^v+x^{2v}+x^{3v}+...)= \sum x^{iv}

對於n個物品,我們將他們每個的多項式都乘起來,所求就是最後那個多項式的前n項的係數。

但是這樣做的複雜度是多少?\small O(n^2logn),明顯過不了。所以我們可能還要優化一下公式。

對於第i個物品的多項式爲:

\small f(i)=\sum x^{j*v_i}= \frac{1}{1 - x^{v_i}}【無窮等比數列求和

所以我們要求的就是:

\small ans = \prod_{i=1}^nf(i)

全是累乘啊。我們把乘法變加法,兩邊同時取對。

\small ln(ans)=\prod_{i=1}^n ln(f(i))= ln(\sum_{i=1}^nf(i))

現在看起來已經差不多了,那麼我們的問題就是f(i)了。因爲我們的物品並不是真的可以取用無限個的,所以這個式子我們還得繼續展開,在合適的時候回到這個限制上。

因爲有:\small lnF(x)=\int\frac{F'(x)}{F(x)}dx

所以我們把f(i)帶進去:【這裏是求\small f(i),直接用\small v代替\small v_i了。這一步求導+積分可以手推一下,下面是個平方項的,後來約掉了

\small ln~f(i)=ln\frac{1}{1-x^{v}}=\int\frac{vx^{v-1}}{1-x^v}dx

裏面好像有個\small f(i),所以我們是時候展開了:【最後這一步積分的還原式手推也會有點點喫力QuQ套求導公式吧。加油

\small \int\frac{vx^{v-1}}{1-x^v}dx=\int \sum_{i=1}^\infty vx^{iv-1}dx=\sum_{i=1}^\infty\frac{1}{i}x^{iv}

至此,就是\small f(i)了。因爲回到上面,我們括號裏面是\small f(i)求和,所以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——

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章