[洛谷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;
}