【NTT】差分与前缀和

题目

题目描述
给定一个长为 nn 的序列 aa,求出其 kk 阶差分或前缀和。
结果的每一项都需要对 10045358091004535809 取模。

输入格式
第一行三个整数 n,k,tn,k,t,若 t=0t=0 表示求前缀和,t=1t=1 表示求差分。
第二行 nn 个整数,表示序列 aa。

输出格式
输出一行 nn 个整数,表示 aa 的 kk 阶差分或前缀和。

输入输出样例
输入 #1复制
8 3 0
1 9 2 6 0 8 1 7
输出 #1复制
1 12 35 76 135 220 332 478
输入 #2复制
8 3 1
1 9 2 6 0 8 1 7
输出 #2复制
1 6 1004535787 26 1004535788 24 1004535780 28
说明/提示
【数据范围】

1 \le n \le 10^51≤n≤10
5

0 \le a_i \le 10^90≤a
i

≤10
9

1\le k \le 10^{2333}, k \not \equiv 0 \pmod{1004535809}1≤k≤10
2333
,k


≡0(mod1004535809)

思路

发现做一次前缀和就相当于与 1 1 1 做一次卷积, k k k 阶前缀和相当于做 k k k 此卷积。由于卷积有结合律,所以我们相当于要卷上 1 ( 1 − x ) k \frac{1}{(1-x)^k} (1x)k1,多项式求逆即可

差分就是这东西的倒数啦

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=333333,mod=1004535809;
int n,tp,k,a[N],lim,l,rev[N],b[N],fac[N],invfac[N];
int add(int a,int b){return a+b<mod?a+b:a+b-mod;}
int sub(int a,int b){return a<b?a-b+mod:a-b;}
int mul(int a,int b){return 1ll*a*b%mod;}
int power(int a,int b)
{
	int ans=1;
	for(;b;b>>=1,a=mul(a,a)) if(b&1) ans=mul(ans,a);
	return ans;
}
void init(int upr)
{
	for(lim=1,l=0;lim<upr;lim<<=1,l++);
	for(int i=0; i<lim; i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<(l-1)); 
}
void dft(int *A,int tp){
	for(int i=0; i<lim; i++) if(i<rev[i]) swap(A[i],A[rev[i]]);
	for(int i=1; i<lim; i<<=1)
		for(int j=0,r=i<<1,Wn=power(3,mod-1+tp*(mod-1)/r); j<lim; j+=r)
			for(int k=0,w=1; k<i; k++,w=mul(w,Wn))
			{
				int x=A[j+k],y=mul(A[i+j+k],w);
				A[j+k]=add(x,y);A[i+j+k]=sub(x,y);
			}
	if(tp==-1)
	{
		int linv=power(lim,mod-2);
		for(int i=0; i<lim; i++) A[i]=mul(A[i],linv);
	}
}
namespace DIF
{
	void solve()
	{
		for(int i=0; i<n; i++) if(i&1) a[i]=(mod-a[i])%mod;
		for(int i=0; i<=k; i++) b[i]=mul(invfac[i],invfac[k-i]);
		init(n<<1);
		dft(a,1);dft(b,1);
		for(int i=0; i<lim; i++) a[i]=mul(a[i],b[i]);
		dft(a,-1);
		for(int i=0; i<n; i++)
		{
			int prod=mul(fac[k],a[i]);
			if(i&1) prod=(mod-prod)%mod;
			printf("%d ",prod);
		} 
	}
}
namespace PRE
{
	void solve()
	{
		for(int i=0; i<n; i++) b[i]=mul(fac[i+k-1],invfac[i]);
		init(n<<1);
		dft(a,1);dft(b,1);
		for(int i=0; i<lim; i++) a[i]=mul(a[i],b[i]);
		dft(a,-1);
		for(int i=0; i<n; i++) printf("%d ",mul(a[i],invfac[k-1]));
	}
}
int main()
{
	scanf("%d%d%d",&n,&k,&tp);
	for(int i=0; i<n; i++) scanf("%d",&a[i]);
	fac[0]=1;
	for(int i=1; i<=n+k; i++) fac[i]=mul(fac[i-1],i);
	invfac[n+k]=power(fac[n+k],mod-2);
	for(int i=n+k-1; i>=0; i--) invfac[i]=mul(invfac[i+1],i+1);
	if(tp) DIF::solve();
	else PRE::solve();
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值