【洛谷P5488】差分与前缀和

题目

题目链接:https://www.luogu.com.cn/problem/P5488
给定一个长为 (n) 的序列 (a),求出其 (k) 阶差分或前缀和。
结果的每一项都需要对 (1004535809) 取模。
(nleq 10^5,1le k le 10^{2333}, k ot equiv 0 pmod{1004535809})

思路

我们把 (a_i) 看做一个多项式的第 (i) 项系数。也就是

[F(x)=sum^{n}_{i=1}a_ix^i ]

前缀和和差分分开来做。

Part 1.前缀和

我们要求它的前缀和。也就是给上式乘上 ((1+x+x^2+x^3+cdots x^n)^k)
由于 (1+x+x^2+x^3+cdots x^n=frac{1}{1-x}),所以我们所求就是 (F imes frac{1}{(1-x)^k})

[frac{1}{(1-x)^k}=sum^{+infty}_{i=0}(-1)^ifrac{(-1)^i·(k+1-i)^underline{i}}{i!}x^i=sum^{+infty}_{i=0}frac{(k-i+1)^underline{i}}{i!}x^i ]

由于

[frac{(k-i+1)^underline{i}}{i!}=egin{pmatrix}k+i-1\iend{pmatrix} ]

所以

[frac{1}{(1-x)^k}=sum^{+infty}_{i=0}egin{pmatrix}k+i-1\iend{pmatrix}x^i ]

组合数递推,然后就是卷积的形式了。上 NTT 即可。

Part 2.差分

同理,等价于求 (F imes (1-x)^k)
由二项式定理得

[(1-x)^k=sum^{+infty}_{i=0}1^{k-i}(-1)^{i}x^iegin{pmatrix}k\iend{pmatrix}=sum^{+infty}_{i=0}(-1)^{i}egin{pmatrix}k\iend{pmatrix}x^i ]

照样可以递推做。然后也是 NTT。

时间复杂度 (O(nlog n))

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int N=300010,MOD=1004535809,G=3,Ginv=334845270;
int n,m,type,len,lim,rev[N];
ll f[N],g[N];
char s[N];

ll fpow(ll x,ll k)
{
	ll ans=1;
	for (;k;k>>=1,x=x*x%MOD)
		if (k&1) ans=ans*x%MOD;
	return ans;
}

void NTT(ll *f,int tag)
{
	for (int i=0;i<lim;i++)
		if (i<rev[i]) swap(f[i],f[rev[i]]);
	for (int k=1;k<lim;k<<=1)
	{
		ll tmp=fpow((tag==1)?G:Ginv,(MOD-1)/(k<<1));
		for (int i=0;i<lim;i+=(k<<1))
		{
			ll w=1;
			for (int j=0;j<k;j++,w=w*tmp%MOD)
			{
				ll x=f[i+j],y=w*f[i+j+k]%MOD;
				f[i+j]=(x+y)%MOD; f[i+j+k]=(x-y)%MOD;
			}
		}
	}
}

int main()
{
	scanf("%d%s%d",&n,s,&type);
	len=strlen(s);
	for (int i=0;i<len;i++) m=(10LL*m+s[i]-48)%MOD;
	for (int i=1;i<=n;i++) scanf("%lld",&f[i]);
	lim=1;
	while (lim<=2*n) lim<<=1;
	for (int i=0;i<lim;i++)
		rev[i]=(rev[i>>1]>>1)|((i&1)?(lim>>1):0);
	g[1]=1;
	if (!type)
	{
		for (int i=1;i<n;i++)
			g[i+1]=g[i]*(m+i-1)%MOD*fpow(i,MOD-2)%MOD;
	}
	else
	{
		for (int i=1;i<n;i++)
			g[i+1]=-g[i]*(m-i+1)%MOD*fpow(i,MOD-2)%MOD;
	}
	NTT(f,1); NTT(g,1);
	for (int i=0;i<lim;i++) f[i]=f[i]*g[i]%MOD;
	NTT(f,-1);
	ll inv=fpow(lim,MOD-2);
	for (int i=2;i<=n+1;i++)
		printf("%lld ",(f[i]*inv%MOD+MOD)%MOD);
	return 0;
}
原文地址:https://www.cnblogs.com/stoorz/p/14234986.html