cunzai_zsy0531

关注我

P5488 差分与前缀和 题解

题面

推式子+二项式定理+FFT。这个题需要一点生成函数的知识。

首先考虑前缀和,求一个多项式 \(F(x)\) 的前缀和相当于是卷上了一个系数全是 \(1\) 的多项式。即

\[\begin{aligned} Sum(x)&=F(x)\times \sum_{i=0}^{\infty}x^i\\ &=F(x)\times \frac{1}{1-x} \end{aligned} \]

那么 \(k\) 阶前缀和即

\[\begin{aligned} Sum(x)&=F(x)\times \frac1{(1-x)^k}\\ &=F(x)\times (1-x)^{-k}\\ &=F(x)\times \sum_{i=0}^{\infty}\binom{k+i-1}{i-1}x^i \end{aligned} \]

使用FFT/NTT优化即可。

考虑到差分是前缀和的逆运算,则

\[\begin{aligned} Dif(x)&=F(x)\times (1-x)^k\\ &=F(x)\times \sum_{i=0}^{\infty}(-1)^i\binom{k-i+1}{i}x^i \end{aligned} \]

同样的道理,直接FTT/NTT即可。

点击查看代码
#include<iostream>
#include<cstdio>
using namespace std;
const int N=4e5+13,P=1004535809,G=3;
inline int qpow(int a,int k){int s=1;for(;k;k>>=1,a=1ll*a*a%P)if(k&1)s=1ll*s*a%P;return s;}
inline int Inv(int a){return qpow(a,P-2);}
const int Gi=Inv(G);
inline int rd(){
	long long res=0;char c=getchar();
	for(;!isdigit(c);c=getchar());
	for(;isdigit(c);c=getchar())res=((res<<1)+(res<<3)+(c-'0'))%P;
	return res;
}
int n,k,op,r[N],a[N],b[N];
inline void fft(int *f,int type,int limit){
	for(int i=0;i<limit;++i)
		if(i<r[i]) swap(f[i],f[r[i]]);
	for(int mid=1;mid<limit;mid<<=1){
		int Wn=qpow(type==1?G:Gi,(P-1)/(mid<<1));
		for(int j=0;j<limit;j+=(mid<<1)){
			int w=1;
			for(int k=0;k<mid;++k,w=1ll*w*Wn%P){
				int x=f[j+k],y=1ll*w*f[j+k+mid]%P;
				f[j+k]=(x+y)%P;f[j+k+mid]=(x-y+P)%P;
			}
		}
	}
}
int main(){
	n=rd(),k=rd(),op=rd();
	for(int i=0;i<n;++i) a[i]=rd();
	b[0]=1;
	if(op){
		for(int i=1;i<n;++i) b[i]=1ll*b[i-1]*(k-i+1+P)%P*Inv(i)%P;
		for(int i=1;i<n;++i)
			if(i&1) b[i]=(-b[i]+P)%P;
	}
	else{
		for(int i=1;i<n;++i) b[i]=1ll*b[i-1]*(k+i-1)%P*Inv(i)%P;
	}
	int limit=1,l=0;
	while(limit<=((n-1)<<1)) limit<<=1,++l;
	for(int i=0;i<limit;++i) r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
	fft(a,1,limit),fft(b,1,limit);
	for(int i=0;i<limit;++i) a[i]=1ll*a[i]*b[i]%P;
	fft(a,-1,limit);int inv=Inv(limit);
	for(int i=0;i<n;++i) printf("%d ",1ll*a[i]*inv%P);
	return 0;
}
posted @ 2022-05-11 18:36  cunzai_zsy0531  阅读(33)  评论(0编辑  收藏  举报