51nod 1258 序列求和 V4 拉格朗日插值法

题目大意:
T(n) = n^k,S(n) = T(1) + T(2) + …… T(n)。给出n和k,求S(n)。
例如k = 2,n = 5,S(n) = 1^2 + 2^2 + 3^2 + 4^2 + 5^2 = 55。
由于结果很大,输出S(n) Mod 1000000007的结果即可。
Input
第1行:一个数T,表示后面用作输入测试的数的数量。(1 <= T <= 500)
第2 - T + 1行:每行2个数,N, K中间用空格分割。(1 <= N <= 10^18, 1 <= K <= 50000)
Output
共T行,对应S(n) Mod 1000000007的结果。
Input示例
3
5 3
4 2
4 1
Output示例
225
30
10

分析:我们知道k次幂的和是一个k+1次多项式,因为他是由 n k + 1 = ( ( n 1 ) + 1 ) k + 1 分解出来的,具体需要二次项定理。
显然这道题要插值了。我们显然可以选相邻的k+2个数,预处理出多项式的前k+2个值,然后插值,这样插值求值可以做到 O ( k ) 。显然最大复杂度就是预处理了,总复杂度为 O ( T k l o g k )

代码:

#include <iostream>
#include <cstdio>
#include <cmath>
#define LL long long

const LL mod=1e9+7;
const int maxn=5e4+7;

using namespace std;

LL n;
LL T,k;
LL a[maxn],p[maxn],q[maxn],jc[maxn],njc[maxn],t[maxn];

LL ksm(LL x,LL y)
{
    if (y==1) return x;
    LL c=ksm(x,y/2);
    c=(c*c)%mod;
    if (y%2) c=(c*x)%mod;
    return c;
}

LL calc(LL n)
{
    if (n<=k+2) return a[n];
    p[0]=1; q[k+3]=1;   
    for (LL i=1;i<=k+2;i++) p[i]=(p[i-1]*(n-i))%mod;
    for (LL i=k+2;i>0;i--) q[i]=(q[i+1]*(n-i))%mod;
    LL ans=0;       
    for (LL i=1;i<=k+2;i++)
    {
        ans=(ans+a[i]*p[i-1]%mod*q[i+1]%mod*njc[k+2-i]%mod*jc[i-1]%mod)%mod;
    }
    return ans;
}

int main()
{
    scanf("%lld",&T);
    jc[0]=1; njc[0]=1;  
    for (LL i=1;i<=50002;i++) jc[i]=jc[i-1]*ksm(i,mod-2)%mod;
    for (LL i=1;i<=50002;i++) njc[i]=njc[i-1]*ksm(mod-i,mod-2)%mod;
    while (T--)
    {
        scanf("%lld%lld",&n,&k);        
        n%=mod;
        for (LL i=1;i<=k+2;i++)
        {
            a[i]=(a[i-1]+ksm(i,k))%mod;
        }
        printf("%lld\n",calc(n));
    }
}

猜你喜欢

转载自blog.csdn.net/liangzihao1/article/details/81022831
今日推荐