P5488-差分与前缀和【NTT,生成函数】
正题
题目链接:https://www.luogu.com.cn/problem/P5488
题目大意
求一个长度为\(n\)的序列的\(k\)阶差分/前缀和。
解题思路
先考虑前缀和怎么做
搞出来生成函数就是
\[(\sum_{i=0}^na_ix^i)*(\sum_{i=0}^{\infty}x^i)^k
\]
然后根据常识我们知道\((\sum_{i=0}^{\infty}x^i)^k=\sum_{i=0}^{\infty}\binom{i+k-1}{i}x^i\),当然也可以理解为\(x^i\)的系数就是每次会往后跳任意格(可以是\(0\)),然后\(k\)次跳\(i\)步的方案数。
之后式子
\[(\sum_{i=0}^na_ix^i)*(\sum_{i=0}^{\infty}\binom{i+k-1}{i}x^i)
\]
直接卷就好了
然后是差分,就是
\[\frac{\sum_{i=0}^na_ix^i}{(\sum_{i=0}^{\infty}x^i)^k}
\]
直接上多项式除法就好 。考虑怎么转换这个式子,我们需要用生成函数的方法了,上等比数列公式就有\((\sum_{i=0}^{\infty}x^i)^k=\frac{1}{(1-x)^k}\)
那么它的倒数就是\((1-x)^k\),上二项式定理就有\(\sum_{i=0}^k(-1)^i\binom{k}{i}x^i\)
然后也是两个式子卷起来就好了。
然后\(k\)可以直接模\(p\),考虑\(f(k)=(\sum_{i=0}^\infty x^i)^k\)那么有\(f(k^n)=f(k)^n\)所以直接让\(k\)模\(p\)即可
时间复杂度\(O(n\log n)\)
\(code\)
#include<cstdio>
#include<algorithm>
#include<cstring>
#define ll long long
using namespace std;
const ll N=(1e5)*6,P=1004535809,g=3;
struct poly{
ll a[N],n;
}G,F;
ll n,k,t,inv[N],r[N];
char s[N];
ll power(ll x,ll b){
ll ans=1;
while(b){
if(b&1)ans=ans*x%P;
x=x*x%P;b>>=1;
}
return ans;
}
void NTT(ll *f,ll op,ll n){
for(ll i=0;i<n;i++)
if(r[i]<i)swap(f[i],f[r[i]]);
for(ll p=2;p<=n;p<<=1){
ll len=(p>>1);
ll tmp=power(g,(P-1)/p);
if(op)tmp=power(tmp,P-2);
for(ll k=0;k<n;k+=p){
ll buf=1;
for(ll i=k;i<k+len;i++){
ll tt=buf*f[i+len]%P;
f[i+len]=(f[i]-tt+P)%P;
f[i]=(f[i]+tt)%P;
buf=buf*tmp%P;
}
}
}
if(op){
int invn=power(n,P-2);
for(int i=0;i<n;i++)
f[i]=f[i]*invn%P;
}
return;
}
void mul(poly &a,poly &b){
ll n=1;
while(n<=a.n+b.n)n<<=1;
for(ll i=0;i<n;i++)
r[i]=(r[i>>1]>>1)^((i&1)?(n>>1):0);
NTT(a.a,0,n);NTT(b.a,0,n);
for(ll i=0;i<n;i++)
a.a[i]=a.a[i]*b.a[i]%P;
NTT(a.a,1,n);
return;
}
int main()
{
scanf("%lld",&n);
scanf("%s",s);ll l=strlen(s);
scanf("%lld",&t);
for(ll i=0;i<l;i++)
k=(k*10+s[i]-'0')%P;
inv[1]=1;
for(ll i=2;i<=n;i++)
inv[i]=(P-(P/i)*inv[P%i]%P)%P;
for(ll i=0;i<n;i++)
scanf("%lld",&F.a[i]);
G.a[0]=1;
for(ll i=1;i<=n;i++){
if(!t)G.a[i]=G.a[i-1]*(i+k-1)%P*inv[i]%P;
else{
if(i>k)break;
G.a[i]=(-1)*G.a[i-1]*(k-i+1)%P*inv[i]%P;
G.a[i]=(G.a[i]+P)%P;
}
}
G.n=F.n=n;
mul(G,F);
for(ll i=0;i<n;i++)
printf("%lld ",G.a[i]);
return 0;
}