Luogu4389 付公主的背包
Luogu4389 付公主的背包
生成函数
对于一个物品,建立生成函数:
\[
\sum_{i=0}^{\infty} x^{iv}\\
=\frac{1}{1-x^v}
\]
把\(n\)个多项式乘起来,时间复杂度显然不太行。
考虑先把多项式进行\(\ln\),加起来,再\(\exp\),这就需要我们快速求\(\ln\),\(\ln \frac{1}{1-x^v}=?\)。
设\(G(x)=\ln F(x)=\ln (1-x^v)\)。
\[G'(x)=\frac{F'(x)}{F(x)}\\
=-vx^{v-1}\frac{1}{1-x^v}\\
=-v x^{v-1} \sum_{i=0}^{\infty} x^{iv}\\
=\sum_{i=1}^{\infty} -vx^{iv-1}\\
\int G'(x)=\int \sum_{i=1}^{\infty} -vx^{iv-1}\\
=\sum_{i=1}^{\infty} \int -vx^{iv-1}\\
=-\sum_{i=1}^{\infty} \frac{1}{i} x^{iv}\\
\ln \frac{1}{1-x^v}\\
=\ln 1-\ln (1-x^v)\\
=\sum_{i=1}^{\infty} \frac{1}{i} x^{iv}
\]
把\(v\)相同的归为一类,时间复杂度上限为\(\sum_{i=1}^m \frac{m}{i} \approx O(m \log m)\)。
再加上一个\(O(m \log m)\)的多项式\(\exp\),总时间复杂度为\(O(m \log m)\)。
\(Code:\)
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define N 400020
#define ll long long
using namespace std;
const int p=998244353;
int n,m,x,s,l,v[N];
int inv[N];
int G[2][25],rev[N];
int a[N],b[N],c[N],d[N],e[N],f[N],g[N],h[N];
void Add(int &x,int y)
{
x=(x+y)%p;
}
void Del(int &x,int y)
{
x=(x-y)%p;
}
void Mul(int &x,int y)
{
x=(ll)x*y%p;
}
int add(int x,int y)
{
return (x+y)%p;
}
int del(int x,int y)
{
return (x-y)%p;
}
int mul(int x,int y)
{
return (ll)x*y%p;
}
int ksm(int x,int y)
{
int ans=1;
while (y)
{
if (y & 1)
Mul(ans,x);
Mul(x,x);
y >>=1;
}
return ans;
}
void Pre()
{
G[0][23]=ksm(3,(p-1)/(1 << 23));
G[1][23]=ksm(G[0][23],p-2);
for (int i=22;i>=1;--i)
{
G[0][i]=mul(G[0][i+1],G[0][i+1]);
G[1][i]=mul(G[1][i+1],G[1][i+1]);
}
}
void NTT(int *a,int t)
{
for (int i=0;i<s;++i)
if (i<rev[i])
swap(a[i],a[rev[i]]);
for (int mid=1,o=1;mid<s;mid <<=1,++o)
for (int j=0;j<s;j+=(mid << 1))
{
int g=1;
for (int k=0;k<mid;++k,Mul(g,G[t][o]))
{
int x=a[j+k],y=mul(g,a[j+k+mid]);
a[j+k]=add(x,y);
a[j+k+mid]=del(x,y);
}
}
}
void solve(int n)
{
s=1,l=0;
while (s<n)
s <<=1,++l;
for (int i=0;i<s;++i)
rev[i]=(rev[i >> 1] >> 1) | ((i & 1) << l-1);
}
void Cut(int *a,int n,int s)
{
memset(a+n,0,(s-n)*sizeof(int));
}
void Dev(int *a,int *b,int n)
{
for (int i=1;i<n;++i)
b[i-1]=mul(a[i],i);
b[n-1]=0;
}
void InvDev(int *a,int *b,int n)
{
b[0]=0;
for (int i=0;i<n;++i)
b[i+1]=mul(a[i],inv[i+1]);
b[n]=0;
}
void GetInv(int *f,int *g,int R)
{
if (R==2)
{
g[0]=ksm(f[0],p-2);
return;
}
GetInv(f,g,R >> 1);
solve(R);
memcpy(c,g,(R >> 2)*sizeof(int)),memcpy(d,f,(R >> 1)*sizeof(int));
NTT(c,0),NTT(d,0);
for (int i=0;i<s;++i)
c[i]=del(add(c[i],c[i]),mul(d[i],mul(c[i],c[i])));
NTT(c,1);
for (int i=0;i<s;++i)
Mul(c[i],inv[s]);
memcpy(g,c,R*sizeof(int)),memset(c,0,s*sizeof(int)),memset(d,0,s*sizeof(int));
}
void GetLn(int *f,int *g,int n)
{
s=1;
while (s<n)
s <<=1;
s <<=1;
GetInv(f,a,s);
Cut(a,n,s);
Dev(f,b,n);
solve(n << 1);
NTT(a,0),NTT(b,0);
for (int i=0;i<s;++i)
Mul(a[i],b[i]);
NTT(a,1);
for (int i=0;i<s;++i)
Mul(a[i],inv[s]);
InvDev(a,g,n);
memset(a,0,s*sizeof(int)),memset(b,0,s*sizeof(int));
}
void GetExp(int *f,int *g,int R)
{
if (R==2)
{
g[0]=1;
return;
}
GetExp(f,g,R >> 1);
GetLn(g,h,R >> 1);
for (int i=0;i<R >> 1;++i)
h[i]=del(f[i],h[i]);
Add(h[0],1);
memcpy(e,g,R*sizeof(int));
solve(R);
NTT(e,0),NTT(h,0);
for (int i=0;i<s;++i)
Mul(e[i],h[i]);
NTT(e,1);
for (int i=0;i<s;++i)
Mul(e[i],inv[s]);
memcpy(g,e,(R >> 1)*sizeof(int));
memset(e,0,s*sizeof(int)),memset(h,0,s*sizeof(int));
}
int main()
{
Pre();
scanf("%d%d",&n,&m);
for (int i=1;i<=n;++i)
scanf("%d",&x),++v[x];
inv[1]=1;
for (int i=2;i<=m+1 << 2;++i)
inv[i]=mul(p-p/i,inv[p%i]);
for (int i=1;i<=m;++i)
if (v[i])
{
for (int j=1;j<=m/i;++j)
Add(f[i*j],mul(inv[j],v[i]));
}
s=1;
while (s<=m)
s <<=1;
s <<=1;
GetExp(f,g,s);
for (int i=1;i<=m;++i)
printf("%d\n",(g[i]%p+p)%p);
return 0;
}