P5488 差分与前缀和 题解
题目描述
给定一个长为 \(n\) 的序列 \(a\) ,求 \(k\) 阶差分或前缀和数组,对 \(1004535809\) 取模。
数据范围
- \(1\le n\le 10^5,0\le a_i\le 10^9,1\le k\le 10^{2333}\) 。
时间限制 \(\texttt{1s}\) ,空间限制 \(\texttt{125MB}\) 。
分析
记 \(f(x)=\sum a_ix^i\) ,则 \(a\) 的 \(k\) 阶前缀和生成函数为 \(\frac{f(x)}{(1-x)^k}\) , \(a\) 的 \(k\) 阶差分生成函数为 \(f(x)\cdot(1-x)^k\) 。
显然可以 \(\ln+\exp\) ,但是没必要。
用广义二项式定理展开,可以得到:
\[(1-x)^k=\sum_{i=0}^k(-1)^i\binom kix^i\\
\frac1{(1-x)^k}=\sum_{i=0}^k\binom{k+i-1}ix^i
\]
组合数可以递推求,时间复杂度 \(\mathcal O(n\log n)\) 。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1<<18,mod=1004535809,inv3=(mod+1)/3;
int k,n,op,len;
int f[maxn],g[maxn],r[maxn];
int read()
{
int q=0;char ch=getchar();
while(!isdigit(ch)) ch=getchar();
while(isdigit(ch)) q=(10ll*q+ch-'0')%mod,ch=getchar();
return q;
}
int qpow(int a,int k)
{
int res=1;
while(k)
{
if(k&1) res=1ll*res*a%mod;
a=1ll*a*a%mod,k>>=1;
}
return res;
}
void ntt(int *a,int n,int op)
{
for(int i=0;i<n;i++) if(i<r[i]) swap(a[i],a[r[i]]);
for(int k=2,m=1;k<=n;k<<=1,m<<=1)
{
int x=qpow(op==1?3:inv3,(mod-1)/k);
for(int i=0;i<n;i+=k)
for(int j=i,w=1;j<i+m;j++)
{
int v=1ll*a[j+m]*w%mod;
a[j+m]=(a[j]-v+mod)%mod,a[j]=(a[j]+v)%mod;
w=1ll*w*x%mod;
}
}
if(op==-1)
{
int inv=qpow(n,mod-2);
for(int i=0;i<n;i++) a[i]=1ll*a[i]*inv%mod;
}
}
int main()
{
n=read(),k=read(),op=read();
for(int i=1;i<=n;i++) f[i]=read();
for(int i=0,cur=1;i<=n;i++)
{
if(op) g[i]=(i&1?mod-1ll:1)*cur%mod,cur=1ll*cur*(k-i)%mod*qpow(i+1,mod-2)%mod;
else g[i]=cur,cur=1ll*cur*(k+i)%mod*qpow(i+1,mod-2)%mod;
}
for(len=1;len<=2*n;len<<=1) ;
for(int i=0;i<len;i++) r[i]=(r[i>>1]>>1)|(i&1?len>>1:0);
ntt(f,len,1),ntt(g,len,1);
for(int i=0;i<len;i++) f[i]=1ll*f[i]*g[i]%mod;
ntt(f,len,-1);
for(int i=1;i<=n;i++) printf("%d ",f[i]);
putchar('\n');
return 0;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/17076424.html
浙公网安备 33010602011771号