【P5488 差分与前缀和】—Monkey can‘t understands this solution
P5488 差分与前缀和 题解
标签: 多项式
前言
一个牛顿二项式定理的应用,掌握好这个题目对组合数学有一定的帮助。
自认为是特别详细了。
前置知识
1、牛顿二项式定理(加个正号防止有歧义)。
2、NTT。
3、生成函数(会封闭形式)。
4、
举个例子:
思路
首先,众所周知的是,将要求前缀和的序列的值依次代到
先从前缀和开始讲起:
设
那么每一项的系数就是序列从
至于
通过牛顿二项式定理把这个式子给拆开。
在这里需要说一下的是,上面坟墓上,当枚举到
1、当为奇数个的时候,上面的式子一看每一项一定是负数
2、当为偶数个的时候,上面可以提出偶数个负号来,全部消掉,而此时
通过上面的讨论可以得到的是:
由此可以得到最终式子,由于
由此可以得到:
式子和递推式都推出来了,猴子应该也会做了,一边裸的
接下来看差分,既然前缀和是
差分其实和前缀和差不多,不过还是来详细推导一下公式吧。
已经化为最简形式了,所以最后再扔一个递推式验算过程:
最后,这个题就圆满结束啦!
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#define int long long
using namespace std;
const int N=4e5+9;
const int mod=1004535809;
const int g[]={3,334845270};
int a[N],b[N];
int r[N];
int term=1,l;
int n,k,t;
int inv[N];
int read()//快读要经过模处理
{
int f=1,x=0;
char s=getchar();
while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}
while(s>='0'&&s<='9'){x=((x<<1)+(x<<3)+(s^'0'))%mod;s=getchar();}
return f*x%mod;
}
int quick(int x,int p)
{
int ret=1;
while(p)
{
if(p&1) ret=ret*x%mod;
x=x*x%mod;
p>>=1;
}
return ret%mod;
}
void NTT(int *A,int type)
{
for(int i=0;i<term;i++)
if(i<r[i])
swap(A[i],A[r[i]]);
for(int mid=1;mid<term;mid<<=1)
{
int R=mid<<1;
int Wn=quick(g[type],(mod-1)/R);
for(int j=0;j<term;j+=R)
{
int w=1;
for(int k=0;k<mid;k++,w=w*Wn%mod)
{
int x=A[j+k]%mod;
int y=w*A[j+k+mid]%mod;
A[j+k]=(x+y)%mod;
A[j+k+mid]=(x-y+mod)%mod;
}
}
}
if(type==1)
{
int inv=quick(term,mod-2);
for(int i=0;i<term;i++)
A[i]=A[i]*inv%mod;
}
return;
}
void less_than_break()()//差分
{
b[0]=1;
inv[1]=1;
for(int i=2;i<=n;i++)
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
for(int i=1;i<=n;i++)
b[i]=-b[i-1]*(k-i+1+mod)%mod*inv[i]%mod;
NTT(a,0);
NTT(b,0);
for(int i=0;i<term;i++)
a[i]=a[i]*b[i]%mod;
NTT(a,1);
for(int i=1;i<=n;i++)
printf("%lld ",a[i-1]);
}
void frontsum()//前缀和
{
b[0]=1;
inv[1]=1;
for(int i=2;i<=n;i++)
inv[i]=(mod-mod/i)*inv[mod%i]%mod;
for(int i=1;i<=n;i++)
b[i]=b[i-1]*(k+i-1)%mod*inv[i]%mod;
NTT(a,0);
NTT(b,0);
for(int i=0;i<term;i++)
a[i]=a[i]*b[i]%mod;
NTT(a,1);
for(int i=1;i<=n;i++)
printf("%lld ",a[i-1]);
}
signed main()
{
n=read();
k=read();
t=read();
for(int i=1;i<=n;i++)
a[i-1]=read();
while(term<=2*n)
{
term<<=1;
l++;
}
for(int i=0;i<term;i++)
r[i]=(r[i>>1]>>1)|((i&1)<<(l-1));
if(t==1) less_than_break();
else frontsum();
return 0;
}