【洛谷P5488】差分与前缀和

题目

题目链接:https://www.luogu.com.cn/problem/P5488
给定一个长为 \(n\) 的序列 \(a\),求出其 \(k\) 阶差分或前缀和。
结果的每一项都需要对 \(1004535809\) 取模。
\(n\leq 10^5,1\le k \le 10^{2333}, k \not \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\times \frac{1}{(1-x)^k}\)

\[\frac{1}{(1-x)^k}=\sum^{+\infty}_{i=0}(-1)^i\frac{(-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!}=\begin{pmatrix}k+i-1\\i\end{pmatrix} \]

所以

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

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

Part 2.差分

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

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

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

时间复杂度 \(O(n\log 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;
}
posted @ 2021-01-05 11:58  stoorz  阅读(144)  评论(0编辑  收藏  举报