洛谷·付公主的背包

初见安~这里是传送门:洛谷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——

发布了158 篇原创文章 · 获赞 23 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/qq_43326267/article/details/103945127
今日推荐