The Sum of the k-th Powers
慢慢推式子,顺便理清思路。
首先这个式子是一个 \(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;
}
一如既往,万事胜意