题目
给定n(1<=n<=1e9),k(0<=k<=1e6),
求 mod(1e9+7)
思路来源
https://blog.csdn.net/BeNoble_/article/details/79512449
题解
所求式为(k+1)次多项式的单点值,需要k+2个点
记f(i)=i^k,先求出f(0)到f(k+2),再前缀和求出sum(0)到sum(k+2)
拉格朗日预处理pre[]suf[]finv[]之后,
对于每个n,用sum(0)到sum(k+2)的值O(k)的线性求即可
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10;
const int mod=1e9+7;
int n,m,d,sg;
ll p[N],cnt;
ll g[N],sum[N];//g[i]=i^k
ll fac[N],finv[N];//分母 阶乘 阶乘逆元
ll pre[N],suf[N];//分子前缀积 分子后缀积
ll ans;
bool ok[N];
ll modpow(ll x,ll n,ll p)
{
ll res=1;
for(;n;x=x*x%p,n/=2)
if(n&1)res=res*x%p;
return res;
}
void init(int mx)
{
//板子部分
fac[0]=pre[0]=suf[mx]=1;
for(ll i=1;i<=mx;++i)
{
fac[i]=fac[i-1]*i%mod;
pre[i]=1ll*(n-i+1)%mod*pre[i-1]%mod;
}
finv[mx]=modpow(fac[mx],mod-2,mod);
for(ll i=mx;i>=1;--i)
{
finv[i-1]=finv[i]*i%mod;
suf[i-1]=1ll*(n-i)%mod*suf[i]%mod;
}
//预处理g[0]到g[mx]部分
g[1]=1;
for(ll i=2;i<=mx;++i)
{
if(!ok[i])
{
p[cnt++]=i;
g[i]=modpow(i,m,mod);
}
for(ll j=0,k;j<cnt&&(k=i*p[j])<=mx;++j)
{
ok[k]=1;
g[k]=g[i]*g[p[j]]%mod;
if(i%p[j]==0)break;
}
}
for(ll i=1;i<=mx;++i)
sum[i]=(sum[i-1]+g[i])%mod;
}
int main()
{
//求前缀和 即插出m+1次多项式前缀和函数的前m+2个点 然后O(m)求单点
scanf("%d%d",&n,&m);//sigma i^m 为m+1次多项式
d=m+1;//d次多项式 d+1个点
init(d);
if(n<=d)printf("%I64d\n",sum[n]);
else
{
for(int i=0;i<=d;++i)
{
sg=(d-i)&1?-1:1;
(ans+=sg*pre[i]%mod*suf[i]%mod*finv[i]%mod*finv[d-i]%mod*sum[i]%mod)%=mod;
}
printf("%I64d\n",(ans+mod)%mod);
}
return 0;
}