LOJ#6261. 一个人的高三楼 组合+NTT
令 $f(i,j)$ 表示 $j$ 的 $i$ 阶前缀和.
那么有 $f(i,j)=\sum_{j=1}^{i} f(i-1,j)$,这个可以直接多项式快速幂.
时间复杂度是 $O(n \log^2 n)$ 或 $O(n \log n)$ ,但是常数大而且第二种不好写.
考虑计算每一个位置 $j \leqslant i$ 对 $i$ 位置的贡献.
令 $p(i,j,k)$ 表示 $j$ 对 $i$ 的 $k$ 阶前缀和的贡献系数,那么有:
$p(i,j,k)=\sum_{t=1}^{i} p(t,j,k-1)$.
组合意义上可以看作将 $j-i$ 分成 $k$ 份的方案数,即 $\binom{j-i+k-1}{k-1}$.
构建生成函数 $B(x)=\sum_{i=0}^{\infty} \binom{i+k-1}{i} x^i$ 然后和 $A(x)=\sum_{i=0}^{n} a_{i} x^i$ 卷一下即可.
code:
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 400009
#define ll long long
#define mod 998244353
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
ll kth;
int A[N],B[N],inv[N],n;
int qpow(int x,int y) {
int tmp=1;
for(;y;y>>=1,x=(ll)x*x%mod)
if(y&1) tmp=(ll)tmp*x%mod;
return tmp;
}
int get_inv(int x) {
return qpow(x,mod-2);
}
void NTT(int *a,int len,int op) {
for(int i=0,k=0;i<len;++i) {
if(i>k) swap(a[i],a[k]);
for(int j=len>>1;(k^=j)<j;j>>=1);
}
for(int l=1;l<len;l<<=1) {
int w=qpow(3,(mod-1)/(l<<1)),wn,x,y;
if(op==-1) w=get_inv(w);
for(int i=0;i<len;i+=l<<1) {
wn=1;
for(int j=0;j<l;++j) {
x=a[i+j],y=(ll)a[i+j+l]*wn%mod;
a[i+j]=(ll)(x+y)%mod;
a[i+j+l]=(ll)(x-y+mod)%mod;
wn=(ll)wn*w%mod;
}
}
}
if(op==-1) {
int p=get_inv(len);
for(int i=0;i<len;++i) {
a[i]=(ll)a[i]*p%mod;
}
}
}
void init() {
inv[1]=1;
for(int i=2;i<N;++i) {
inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
}
inv[0]=1;
for(int i=1;i<N;++i) {
inv[i]=(ll)inv[i-1]*inv[i]%mod;
}
ll mul=1;
for(int i=0;i<=n;++i) {
B[i]=(ll)mul%mod*inv[i]%mod;
mul=mul*1ll*((kth+i)%mod)%mod;
}
}
int main() {
// setIO("input");
scanf("%d%lld",&n,&kth);
init();
for(int i=1;i<=n;++i) scanf("%d",&A[i]);
int l=1;
for(;l<(2*n+1);l<<=1);
NTT(A,l,1),NTT(B,l,1);
for(int i=0;i<l;++i) {
A[i]=(ll)A[i]*B[i]%mod;
}
NTT(A,l,-1);
for(int i=1;i<=n;++i) {
printf("%d\n",A[i]);
}
return 0;
}

浙公网安备 33010602011771号