测试地址:The Sum of the k-th Powers
题目大意: 求
对
取模的值。
做法: 本题需要用到拉格朗日插值。
容易看出(或者用数学归纳法简单证明),答案
是一个关于
的最高次为
的多项式。问题是,怎么得到这个多项式呢?这时候就要使用拉格朗日插值法。
拉格朗日插值法是一个可以由
个二维平面上的点
,构造出一个正好穿过这些点的
次函数
的算法。有:
其中
称为插值基函数,其表达式为
。
为什么有这个式子呢?首先看存在性,注意到
当且仅当
时有取值
,否则当
时取值为
,那么显然上面的函数可以穿过对应的点。至于唯一性好像要用一些玄学的东西证,你只需要知道
个点一定能确定一个
次函数就行了…
回到这一题,我们显然可以用
这些点来使用拉格朗日插值法,算出这些点显然是
的(或者你可以用线性筛优化到
…)。接下来我们把
代入上式:
后面的积式中,分子和分母可以分别预处理,分子只要处理出前缀积和后缀积即可(不能用
这个式子,因为
有可能为
),而对于分母,我们发现分母可以拆成两个类似于下面这样的部分:
和
,分别预处理出来即可,时间复杂度为
(或者用线性求逆元优化成
)。
所以我们就解决了此题,时间复杂度为
(可以优化到
)。
以下是本人代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod=1000000007;
ll n,k,invp[1000010],invn[1000010],inv[1000010];
ll pre[1000010],suf[1000010];
ll power(ll a,ll b)
{
ll s=1,ss=a;
while(b)
{
if (b&1) s=s*ss%mod;
ss=ss*ss%mod;
b>>=1;
}
return s;
}
int main()
{
scanf("%lld%lld",&n,&k);
invp[0]=invn[0]=1;
pre[0]=n;
for(ll i=1;i<=k+1;i++)
{
invp[i]=invp[i-1]*power(i,mod-2)%mod;
invn[i]=invn[i-1]*power(mod-i,mod-2)%mod;
pre[i]=pre[i-1]*(n-i+mod)%mod;
}
suf[k+1]=(n-k-1+mod)%mod;
for(int i=k;i>=0;i--)
suf[i]=suf[i+1]*(n-i+mod)%mod;
ll ans=0,now=0;
for(int i=0;i<=k+1;i++)
{
if (k>0||i>0) now=(now+power(i,k))%mod;
ll tot=1;
if (i>0) tot=tot*pre[i-1]%mod;
if (i<k+1) tot=tot*suf[i+1]%mod;
ll nowtot=tot*invp[i]%mod*invn[k-i+1]%mod;
ans=(ans+now*nowtot)%mod;
}
printf("%lld",ans);
return 0;
}