bzoj3157&&3516&&4126 国王奇遇记【拉格朗日差值+高阶差分】

Description

这里写图片描述

Input

共一行包括两个正整数N和M。

Output

共一行为所求表达式的值对10^9+7取模的值。

Sample Input

5 3

Sample Output

36363

HINT

1<=N<=10^9,1<=M<=500000

解题思路:

说一个不仅能求原题,还能求其拓展形式 i=0nf(i)qi (f(i)k) ∑ i = 0 n f ( i ) q i   ( f ( i ) 是 一 个 k 次 多 项 式 ) O(k) O ( k ) 算法。
(最好 f(i) f ( i ) 可以快速算或以点值形式给出,当然dalao们可以直接上多项式多点求值)

首先 q=1 q = 1 时还是要特判。
q1 q ≠ 1 时,我们设 S(n)=i=0n1f(i)qi S ( n ) = ∑ i = 0 n − 1 f ( i ) q i ,所以我们要求 S(n+1) S ( n + 1 )
有一个结论:存在一个 k ≤ k 次多项式 g(x) g ( x ) 使得 S(n)=qng(n)g(0) S ( n ) = q n g ( n ) − g ( 0 )
考虑用归纳法证明:
n=0 n = 0 时,显然成立。
n=1 n = 1 时, S(n)=f(0)=qg(1)g(0) S ( n ) = f ( 0 ) = q g ( 1 ) − g ( 0 ) g(1)=(f(0)+g(0))/q g ( 1 ) = ( f ( 0 ) + g ( 0 ) ) / q ,也成立。
假设 nm n ≤ m 时都成立,那么当 n=m+1 n = m + 1
qS(n)=i=0n1f(i)qi+1=qn+1g(n)qg(0) q S ( n ) = ∑ i = 0 n − 1 f ( i ) q i + 1 = q n + 1 g ( n ) − q g ( 0 )
S(n+1)=i=0nf(i)qi=qn+1g(n+1)g(0) S ( n + 1 ) = ∑ i = 0 n f ( i ) q i = q n + 1 g ( n + 1 ) − g ( 0 )
两式相减可得 i=1n(f(i)f(i1))qi+f(0)=qn+1(g(n+1)g(n))+(q1)g(0) ∑ i = 1 n ( f ( i ) − f ( i − 1 ) ) q i + f ( 0 ) = q n + 1 ( g ( n + 1 ) − g ( n ) ) + ( q − 1 ) g ( 0 )
而左边 i=1n1(f(i)f(i1))qi+f(0)+(f(n)f(n1))qn=qn(g(n)g(n1))+(q1)g(0)+(f(n)f(n1))qn ∑ i = 1 n − 1 ( f ( i ) − f ( i − 1 ) ) q i + f ( 0 ) + ( f ( n ) − f ( n − 1 ) ) q n = q n ( g ( n ) − g ( n − 1 ) ) + ( q − 1 ) g ( 0 ) + ( f ( n ) − f ( n − 1 ) ) q n

代入原式,两边同除以 qn q n 可得: g(n)g(n1)+f(n)f(n1)=q(g(n+1)g(n)) g ( n ) − g ( n − 1 ) + f ( n ) − f ( n − 1 ) = q ( g ( n + 1 ) − g ( n ) )

所以 g(n)=((q+1)g(n)g(n1)+f(n)f(n1))/q g ( n ) = ( ( q + 1 ) g ( n ) − g ( n − 1 ) + f ( n ) − f ( n − 1 ) ) / q 也是个 k ≤ k 次多项式。得证。

现在问题就是如何求 g(x) g ( x )

S(n)S(n1)=qng(n)qn1g(n1)=qn1f(n1) S ( n ) − S ( n − 1 ) = q n g ( n ) − q n − 1 g ( n − 1 ) = q n − 1 f ( n − 1 )
即是 g(n)=(g(n1)+f(n1))/q g ( n ) = ( g ( n − 1 ) + f ( n − 1 ) ) / q

所以如果我们知道了 g(0) g ( 0 ) 的值,就能推算出 g(1),g(2),...g(k+1) g ( 1 ) , g ( 2 ) , . . . g ( k + 1 )

不妨设 g(0)=x g ( 0 ) = x ,由递推式可知 g(1),g(2),g(k+1) g ( 1 ) , g ( 2 ) , g ( k + 1 ) 都可表示为一次函数的形式。

k k 次多项式的k+1次差分为0,所以可以列出 k+1 k + 1 阶差分表达式: i=0k+1(1)i(k+1i)g(k+1i)=0 ∑ i = 0 k + 1 ( − 1 ) i ( k + 1 i ) g ( k + 1 − i ) = 0 从而解出 g(0) g ( 0 )

然后直接朗格朗日差值求出 g(n+1) g ( n + 1 ) 就可以得到 S(n+1) S ( n + 1 ) 了。

代码如下,其实很简洁:

#include<bits/stdc++.h>
#define ll long long
using namespace std; 

ll getll()
{
    ll i=0,f=1;char c;
    for(c=getchar();(c!='-')&&(c<'0'||c>'9');c=getchar());
    if(c=='-')f=-1,c=getchar();
    for(;c>='0'&&c<='9';c=getchar())i=(i<<3)+(i<<1)+c-'0';
    return i*f;
}

const int N=500005,mod=1e9+7;
ll n,m;
ll f[N],a[N][2],g[N],fac[N],inv[N],pre[N],suf[N];

int Pow(ll x,ll y)
{
    ll res=1;
    for(;y;y>>=1,x=x*x%mod)
        if(y&1)res=res*x%mod;
    return res;
}

inline int C(int x,int y)
{
    return fac[x]*inv[y]%mod*inv[x-y]%mod;
}

int G(ll x)
{
    if(x<m)return g[x];
    ll res=0,t=1;suf[m+1]=1;x%=mod;
    for(int i=0;i<=m;i++)pre[i]=t*(x-i)%mod,t=pre[i];
    for(int i=m;i>=0;i--)suf[i]=suf[i+1]*(x-i)%mod;
    for(int i=0;i<=m;i++)
    {
        ll t=suf[i+1]*(i==0?1:pre[i-1])%mod;
        t=t*g[i]%mod*inv[i]%mod*(((m-i)&1)?mod-inv[m-i]:inv[m-i])%mod;
        res=(res+t)%mod;
    }
    return (res+mod)%mod;
}

int main()
{
    //freopen("lx.in","r",stdin); 
    scanf("%lld%lld",&n,&m);
    if(m==1)
    {
        printf("%lld\n",n*(n+1)%mod*Pow(2,mod-2)%mod);
        return 0;
    }
    for(int i=0;i<=m;i++)f[i]=Pow(i,m);
    fac[0]=1;
    for(int i=1;i<=m+1;i++)fac[i]=fac[i-1]*i%mod;
    inv[m+1]=Pow(fac[m+1],mod-2);
    for(int i=m;i>=0;i--)inv[i]=inv[i+1]*(i+1)%mod;
    ll t=Pow(m,mod-2);a[0][1]=1,a[0][0]=0;
    for(int i=1;i<=m+1;i++)
        a[i][1]=a[i-1][1]*t%mod,a[i][0]=(a[i-1][0]+f[i-1])*t%mod;
    ll c=0,d=0;
    for(int i=0;i<=m+1;i++)
    {
        c=(c+a[m+1-i][1]*C(m+1,i)*((i&1)?1:-1))%mod;
        d=(d+a[m+1-i][0]*C(m+1,i)*((i&1)?1:-1))%mod;
    }
    g[0]=(-d)*Pow(c,mod-2)%mod;
    for(int i=1;i<=m+1;i++)g[i]=(g[i-1]+f[i-1])*t%mod;
    ll ans=G(n+1); 
    ans=(ans*Pow(m,n+1)%mod-g[0]+mod)%mod;
    printf("%lld\n",ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值