题目
思路
这是题解的思路:传送门
我就再具体写写我对题解的几点理解吧。
- 首先,解决这个递推式的问题:
a n s i + 1 , j = ∑ k = 1 m C a + b b − k + j a n s i , k ans_{i+1,j}=\sum^{m}_{k=1}{C^{b-k+j}_{a+b} ans_{i,k}} ansi+1,j=k=1∑mCa+bb−k+jansi,k
- 解释一下
i,j,k
的意义吧。i,j
指现在讨论从第i
列走到第i+1
列的顺序第j
个位置;k
代表枚举从第i
列的顺序第k
个位置出发。
- 那么我们先看从
(i,k)
到(i+1,j)
的贡献,枚举前 a 次向下的次数t
,就可得出一个这样的贡献:
∑ k = 0 j + b − k C a t C b j − k + b − t a n s i , k \sum^{j+b-k}_{k=0} {C^t_a C^{j-k+b-t}_b} ans_{i,k} k=0∑j+b−kCatCbj−k+b−tansi,k
通过范德蒙德卷积可以得到这个式子实际上等于
C a + b j − k + b a n s i , k C^{j-k+b}_{a+b} ans_{i,k} Ca+bj−k+bansi,k
那么对k
求个和就是最上面那个递推式了。
关于范德蒙德卷积可以参考这个博客 https://blog.csdn.net/qq_42880894/article/details/87814601
有了这个递推式,正如题解所说,对每个 i 算完的时间复杂度是 O(n^2)
,那么总的时间复杂度应该是 O(n^3)
,对于 1000 来说可能会超时,于是就得用 NTT 加速了。
- 接下来就是我对这题 NTT 的理解(现学 QAQ)
代码
初代(未用NTT加速,会TLE)
#include <cstdio>
#define N 1005
#define Ha 998244353
typedef long long LL;
int n,h,a[N],b[N];
LL jc[10005]; //阶乘
LL ans[N][N<<3];
//快速幂
LL ksm(LL x, LL t) {
LL ret=1;
for (; t; (t>>=1),(x=x*x%Ha)) {
if (t&1) {
ret=ret*x%Ha;
}
}
return ret;
}
//组合数
LL C(LL x, LL y) {
if (y>x || y<0) {
return 0;
}
if (x-y>y) {
y=x-y;
}
LL ret;
ret=jc[x];
ret=ret*ksm(jc[y],Ha-2)%Ha;
ret=ret*ksm(jc[x-y],Ha-2)%Ha;
return ret;
}
int main() {
jc[0]=jc[1]=1;
for (int i=2; i<=10000; i++) {
jc[i]=(jc[i-1]*i)%Ha;
}
scanf("%d",&n);
for (int i=1; i<=n; i++) {
scanf("%d%d",&a[i],&b[i]);
}
ans[0][1]=1;
h=1;
for (int i=1; i<=n; i++) {
for (int j=1,mj=h+a[i]-b[i]; j<=mj; j++) {
LL tmp=0;
for (int k=1; k<=h; k++) {
tmp+=ans[i-1][k]*C(a[i]+b[i],j-k+b[i])%Ha; //核心
tmp%=Ha;
}
ans[i][j]+=tmp;
ans[i][j]%=Ha;
}
h+=a[i]-b[i];
}
LL Ans=0;
for (int i=1; i<=h; i++) {
Ans+=ans[n][i];
Ans%=Ha;
}
printf("%lld\n",Ans);
return 0;
}
/*
2
4 2
2 3
*/