Loj 556 咱们去烧菜吧

泰勒展开 + 多项式 $\exp$ 处理多重背包计数.

这题的数据有锅,原本的题面是 $a_i>0$ 的,但 std 数据造错了,造出了 $a_i=0$ 的数据,甚至有 $a_i=b_i=0$ 导致答案为 $\infty$ 的数据,但管理员修的时候以为是题面数据范围写错了,就把题面改掉了.

$a_i=0$ 可以直接忽略它,最后将方案数乘上 $b_i+1$ ,于是在接下来的分析以及代码中都不考虑 $a_i=0$ 的情况.

无限背包可以转化成多重背包,将 $b_i$ 调整为最多能放下的物品数目即可,于是只用考虑多重背包的计数.

考虑写出答案的生成函数 $A(x)$ ,
$$
A(x)=\prod_{i=1}^m (\sum_{k=0}^{b_i} x^{k\cdot a_i})=\prod_{i=1}^m \frac{1-x^{(b_i+1)\cdot a_i}}{1-x^{a_i}}
$$
直接求乘积需要先做 $m$ 次多项式求逆,时间复杂度不能接受.

考虑将两边同时取对数,得到
$$
\ln A(x)=\sum_{i=1}^{m} \ln(1-x^{(b_i+1)\cdot a_i})-\ln(1-x^{a_i})
$$
设 $f(k)=\ln(1-x^k)$ ,将它在 $x_0=0$ 处做泰勒展开,得到
$$
f(k)=\ln(1-x^k)=-\sum_{i=1}^{\infty} \frac{x^{ki}}{i}
$$
把相同的 $\ln (1-x^k)$ 合并同类项后,对每个 $k$ ,暴力枚举 $i$ ,将次数 $\le n$ 的贡献加入 $\ln A(x)$ 中.

每个 $k$ 需要枚举的次数是 $\frac{n}{k}$ ,而最多只有 $2m$ 个不相同的 $k$ 需要枚举.

将 $m,n$ 看做同阶,根据调和级数的求和,在这一步就可以用 $O(n\log n)$ 的时间复杂度求出 $\ln A(x)$ .

最后再做一次多项式 $\exp$ 得到 $A(x)$ .

时间复杂度 $O(n\log n)$ .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
//%std
#include<bits/stdc++.h>
#define endl '\n'
using namespace std;
typedef long long ll;
inline int read()
{
int out=0,fh=1;
char jp=getchar();
while ((jp>'9'||jp<'0')&&jp!='-')
jp=getchar();
if (jp=='-')
fh=-1,jp=getchar();
while (jp>='0'&&jp<='9')
out=out*10+jp-'0',jp=getchar();
return out*fh;
}
const int P=998244353,G=3;
int add(int a,int b)
{
return (a+b>=P)?(a+b-P):(a+b);
}
void inc(int &a,int b)
{
a=add(a,b);
}
int mul(int a,int b)
{
return 1LL * a * b % P;
}
int fpow(int a,int b)
{
int res=1;
while(b)
{
if(b&1)
res=mul(res,a);
a=mul(a,a);
b>>=1;
}
return res;
}
const int MAXN=4e5+10;
int rev[MAXN],omega[MAXN],inv[MAXN],curn=0;
void init(int n)
{
if(curn==n)
return;
for(int i=0;i<n;++i)
rev[i]=(rev[i>>1]>>1)|((i&1)*(n>>1));
for(int l=2;l<=n;l<<=1)
{
omega[l]=fpow(G,(P-1)/l);
inv[l]=fpow(omega[l],P-2);
}
curn=n;
}
void DFT(int *a,int n,bool invflag)
{
init(n);
for(int i=0;i<n;++i)
if(i<rev[i])
swap(a[i],a[rev[i]]);
for(int l=2;l<=n;l<<=1)
{
int m=l>>1;
int gi=omega[l];
if(invflag)
gi=inv[l];
for(int *p=a;p!=a+n;p+=l)
{
int g=1;
for(int i=0;i<m;++i)
{
int t=mul(g,p[i+m]);
p[i+m]=add(p[i],P-t);
p[i]=add(p[i],t);
g=mul(g,gi);
}
}
}
if(invflag)
{
int invn=fpow(n,P-2);
for(int i=0;i<n;++i)
a[i]=mul(a[i],invn);
}
}
void NTT(int *A,int *B,int *C,int lenA,int lenB)
{
int lenC=lenA+lenB-1,n=1;
while(n<lenC)
n<<=1;
static int a[MAXN],b[MAXN];
copy(A,A+lenA,a);
fill(a+lenA,a+n,0);
copy(B,B+lenB,b);
fill(b+lenB,b+n,0);
DFT(a,n,false);
DFT(b,n,false);
for(int i=0;i<n;++i)
C[i]=mul(a[i],b[i]);
DFT(C,n,true);
}
void PolyInverse(int *A,int *B,int N) // B=A^(-1)
{
int n=1;
while(n<N)
n<<=1;
static int res[MAXN],tmp[MAXN];
res[0]=fpow(A[0],P-2);
for(int i=2;i<=n;i<<=1)
{
NTT(A,res,tmp,i,i);
NTT(tmp,res,tmp,i,i);
for(int j=0;j<i;++j)
res[j]=add(mul(2,res[j]),P-tmp[j]);
}
copy(res,res+N,B);
}
void PolyDiff(int *A,int n)
{
for(int i=0;i<n-1;++i)
A[i]=mul(i+1,A[i+1]);
A[n-1]=0;
}
int Inv[MAXN];
void PolyInt(int *A,int n)
{
for(int i=n+1;i>=1;--i)
A[i]=mul(Inv[i],A[i-1]);
A[0]=0;
}
void PolyLn(int *A,int *B,int n) // B=ln(A)
{
static int invA[MAXN],tmp[MAXN];
PolyInverse(A,invA,n);
copy(A,A+n,tmp);
PolyDiff(tmp,n);
NTT(tmp,invA,tmp,n,n);
PolyInt(tmp,n);
copy(tmp,tmp+n,B);
}
void PolyExp(int *A,int *B,int N) // B=exp(A)
{
int n=1;
while(n<N)
n<<=1;
static int res[MAXN],tmp[MAXN];
res[0]=1;
for(int i=2;i<=n;i<<=1)
{
PolyLn(res,tmp,i);
for(int j=0;j<i;++j)
tmp[j]=add(A[j],P-tmp[j]);
inc(tmp[0],1);
NTT(tmp,res,res,i,i);
}
copy(res,res+N,B);
}
int n,m,t[MAXN],A[MAXN];
int main()
{
n=read(),m=read();
int N=1;
while(N<n+1)
N<<=1;
Inv[1]=1;
for(int i=2;i<N;++i)
Inv[i]=mul(P/i,add(P,-Inv[P%i]));
for(int i=0;i<m;++i)
{
int a=read(),b=read();
inc(t[a],1);
if(b && a<=n/(b+1))
inc(t[a*(b+1)],P-1);
}
for(int k=1;k<=n;++k)
if(t[k])
for(int i=1;i*k<=n;++i)
inc(A[i*k],mul(t[k],Inv[i]));
PolyExp(A,A,n+1);
for(int i=1;i<=n;++i)
printf("%d\n",A[i]);
puts("");
return 0;
}