[洛谷P4389] 付公主的背包

前言

经典题。

题目

洛谷

讲解

首先对 \(998244353\) 条件反射,然后发现答案是 \(\prod \frac{1}{1-x^{v_{i}}}\)

但是直接做是 \(O(nm\log_2m)\) 的,甚至跑不过背包,接下来就是NB优化。

我们暂且先不考虑倒数,令 \(F(x)=1-x^{V}\),幂次的累乘可以自然想到取对数后转为累加。

\(\ln(F(x))=G(x)\)

\[\begin{aligned}(\ln(F(x)))'&=G'(x)\\\frac{F'(x)}{F(x)}&=G'(x)\\-\frac{Vx^{V-1}}{1-x^{V}}&=G'(x)\\-\sum_{i=0}Vx^{V-1+Vi}&=G'(x)\\-\sum_{i=0}\frac{Vx^{V+V_{i}}}{V+Vi}&=G(x)\\-\sum_{i=0}\frac{x^{V+Vi}}{1+i}&=G(x)\\-\sum_{i=1}\frac{x^{Vi}}{i}&=G(x)\\\end{aligned} \]

看看我们得到了什么!\(\ln(1-x^V)=-\sum_{i=1}\frac{x^{Vi}}{i}\)

显然后面那个东西的和可以直接在 \(O(m\ln m)\) 的时间里把所有系数都预处理出来,然后做个exp,最后求逆就是答案。

时间复杂度 \(O(m\ln m)\)

代码

人傻常数大
//12252024832524
#include <bits/stdc++.h>
#define TT template<typename T>
using namespace std;

typedef long long LL;
const int MAXN = 1 << 18 | 5;
const int MOD = 998244353;
const int PHI = 998244352;
const int GINV = 332748118,G = 3;
int n,m;
int a[MAXN],b[MAXN],ans[MAXN],cnt[MAXN];

LL Read()
{
	LL x = 0,f = 1; char c = getchar();
	while(c > '9' || c < '0'){if(c == '-') f = -1;c = getchar();}
	while(c >= '0' && c <= '9'){x = (x*10) + (c^48);c = getchar();}
	return x * f;
}
TT void Put1(T x)
{
	if(x > 9) Put1(x/10);
	putchar(x%10^48);
}
TT void Put(T x,char c = -1)
{
	if(x < 0) putchar('-'),x = -x;
	Put1(x); if(c >= 0) putchar(c);
}
TT T Max(T x,T y){return x > y ? x : y;}
TT T Min(T x,T y){return x < y ? x : y;}
TT T Abs(T x){return x < 0 ? -x : x;}

int inv[MAXN];
void init()
{
	inv[0] = inv[1] = 1;
	for(int i = 2;i < MAXN;++ i) inv[i] = 1ll * inv[MOD%i] * (MOD - MOD/i) % MOD;
}
int qpow(int x,int y)
{
	int ret = 1;
	while(y){if(y & 1) ret = 1ll * ret * x % MOD;x = 1ll * x * x % MOD;y >>= 1;}
	return ret;
}
int rev[MAXN],len;
int pre(int L)
{
	int l = -1; len = 1;
	while(len < (L<<1)) len <<= 1,++l;
	for(int i = 1;i < len;++ i) rev[i] = (rev[i>>1] >> 1) | ((i & 1) << l);
	return len;
}
int Ad(int x){if(x >= MOD) x -= MOD;if(x < 0) x += MOD;return x;}
void NTT(int *a,int f)
{
	for(int i = 0;i < len;++ i) if(i < rev[i]) swap(a[i],a[rev[i]]);
	for(int i = 1;i < len;i <<= 1)
	{
		int w = qpow(f > 0 ? G : GINV,PHI / (i<<1));
		for(int j = 0,p = i << 1;j < len;j += p)
			for(int k = 0,mi = 1;k < i;++ k,mi = 1ll * mi * w % MOD)
			{
				int X = a[j+k],Y = 1ll * mi * a[i+j+k] % MOD;
				a[j+k] = Ad(X+Y);
				a[i+j+k] = Ad(X-Y+MOD);
			}
	}
	if(f == -1) for(int i = 0,in = qpow(len,MOD-2);i < len;++ i) a[i] = 1ll * a[i] * in % MOD;
}
int tmp1[MAXN],tmp2[MAXN];
void polymul(int *a,int lena,int *b,int lenb,int *ret)//巨慢
{
	pre(Max(lena,lenb));
	for(int i = 0;i < len;++ i) if(i < lena) tmp1[i] = a[i]; else tmp1[i] = 0;
	for(int i = 0;i < len;++ i) if(i < lenb) tmp2[i] = b[i]; else tmp2[i] = 0;
	NTT(tmp1,1); NTT(tmp2,1);
	for(int i = 0;i < len;++ i) ret[i] = 1ll * tmp1[i] * tmp2[i] % MOD;
	NTT(ret,-1);
}
void polyinv(int *a,int *ret,int N)//还行 
{
	if(N == 1){ret[0] = qpow(a[0],MOD-2);return;}
	polyinv(a,ret,(N+1)>>1); pre(N);
	for(int i = 0;i < len;++ i) if(i < N) tmp1[i] = a[i]; else tmp1[i] = 0;
	NTT(tmp1,1); NTT(ret,1);
	for(int i = 0;i < len;++ i) ret[i] = (2ll * ret[i] - 1ll * tmp1[i] * ret[i] % MOD * ret[i] % MOD + MOD) % MOD;
	NTT(ret,-1); for(int i = N;i < len;++ i) ret[i] = 0;
}
int ad[MAXN],tmp3[MAXN];
void polyln(int *a,int *ret,int N)//还行 
{
	pre(N); for(int i = 0;i < len;++ i) ad[i] = tmp3[i] = 0;
	for(int i = 1;i < N;++ i) ad[i-1] = 1ll * i * a[i] % MOD;//导 
	polyinv(a,tmp3,N);
	polymul(ad,N,tmp3,N,ret);
	for(int i = N-1;i >= 1;-- i) ret[i] = 1ll * ret[i-1] * inv[i] % MOD;//积 
//	for(int i = N;i < len;++ i) ret[i] = 0;
	ret[0] = 0;
}
int tmp4[MAXN],tmp5[MAXN];
void polyexp(int *a,int *ret,int N)//还行 
{
	if(N == 1) {ret[0] = 1;return;}//a[0] = 0;
	polyexp(a,ret,(N+1) >> 1); 
	polyln(ret,tmp4,N); --tmp4[0]; //pre(N);
	for(int i = 0;i < len;++ i) if(i < N) tmp4[i] = (MOD-tmp4[i]+a[i]) % MOD; else tmp4[i] = 0;
	polymul(ret,N,tmp4,N,ret);
//	for(int i = N;i < len;++ i) ret[i] = 0;
}

int main()
{
//	freopen(".in","r",stdin);
//	freopen(".out","w",stdout);
	init();
	n = Read(); m = Read();
	for(int i = 1;i <= n;++ i) ++cnt[Read()];
	for(int i = 1;i <= m;++ i)
		if(cnt[i])
			for(int j = i,k = 1;j <= m;j += i,++ k)
				a[j] = (a[j] - 1ll * cnt[i] * inv[k] % MOD + MOD) % MOD;
	polyexp(a,b,m+1);
	polyinv(b,ans,m+1);
	for(int i = 1;i <= m;++ i) Put(ans[i],'\n');
	return 0;
}
原文地址:https://www.cnblogs.com/PPLPPL/p/15777894.html