The Sum of the k-th Powers

link

慢慢推式子,顺便理清思路。

首先这个式子是一个 \(k+1\) 次多项式,而拉格朗日差值是可以做这种给定 \(k+2\) 个点然后求在这条曲线上另一个点纵坐标的事情的。

方便起见直接用 \([1,k+2]\) 的函数值来做。假如某处的函数值为 \(s(i)\) ,由拉插的公式可得:

\[f(m)=\sum\limits_{i=1}^{k+2}s_i\prod\limits_{j\ne i}\frac{m-j}{i-j}=\sum\limits_{i=1}^{k+2}s_i\frac{\prod\limits_{j\ne i}(m-j)}{\prod\limits_{j\ne i}(i-j)} \]

\(O(N^2)\)会当场去世,考虑优化。

会发现 \(s_i\) 似乎并没有办法优化,所以求和部分是硬消耗,于是考虑如何快速求得后面那个分式的值。

于是惊奇而又开心地发现分子分母都是等差数列,而且是连续整数的乘积。于是分别分析。分子部分:

\[\prod\limits_{j\ne i}(m-j)=\prod\limits_{j=1}^{i-1}(m-j)\prod\limits_{j=i+1}^{k+2}(m-j) \]

可以考虑维护 \(m-j\) 的前缀后缀积(后缀的话从 \(j=k+2\) 开始),即可快速求解。分子部分则更加简单:

\[ss(i)=\prod\limits_{j=1}^ij \]

\[\prod\limits_{j\ne i}(i-j)=\prod\limits_{j=1}^{i-1}j\prod\limits_{j=i-k-2}^{-1}j=ss(i-1)ss(k+2-i)(-1)^{k+2-1} \]

于是就都可以预处理啦。

代码异常简洁(核心代码不到200字),跑得也很快。写得很顺,开心快乐。

#include<cstdio>
#define zczc
#define int long long
const int N=1000010;
const int mod=1e9+7;
inline void read(int &wh){
	wh=0;int f=1;char w=getchar();
	while(w<'0'||w>'9'){if(w=='-')f=-1;w=getchar();}
	while(w>='0'&&w<='9'){wh=wh*10+w-'0';w=getchar();}
	wh*=f;return;
}

inline int pow(int s1,int s2){
	if(s2==1)return s1;
	if(s2==0)return 1;
	int an=pow(s1,s2>>1);
	if(s2&1)return an*an%mod*s1%mod;
	else return an*an%mod;
}

int m,k,sum,ans,a[N],b[N],c[N];

signed main(){
	
	#ifdef zczc
	freopen("in.txt","r",stdin);
	#endif
	
	read(m);read(k);
	
	a[0]=b[0]=c[k+3]=1;
	for(int i=1;i<=k+2;i++)a[i]=a[i-1]*i%mod;
	for(int i=1;i<=k+2;i++)b[i]=b[i-1]*(m-i)%mod;
	for(int i=k+2;i;i--)c[i]=c[i+1]*(m-i)%mod;
	
	for(int i=1;i<=k+2;i++){
		sum+=pow(i,k);sum%=mod;
		int u=b[i-1]*c[i+1]%mod;
		int v=a[i-1]*a[k+2-i]*((k-i)%2?-1:1)%mod;
		ans+=sum*u%mod*pow(v,mod-2)%mod;ans%=mod;
	}
	printf("%lld",(ans+mod)%mod);
	
	return 0;
}
posted @ 2022-04-30 11:13  Feyn618  阅读(23)  评论(0编辑  收藏  举报