2019 ICPC 邀请赛(南昌) B-Polynomial(拉格朗日插值法)

题面

定义了 f ( x ) = a 0 + a 1 x 1 + a 2 x 2 + ⋯ + a n x n f_{(x)}=a_0+a_1x^1+a_2x^2+\cdots+a_nx^n f(x)=a0+a1x1+a2x2++anxn.数字可能会非常大,所以对9999991取模。
对于一个多项式,XH不知道任意一个 a i a_i ai,但是他知道 f ( 0 ) , f ( 1 ) , f ( 2 ) ⋯ f ( n ) f_{(0)},f_{(1)},f_{(2)}\cdots f_{(n)} f(0),f(1),f(2)f(n)的值。他想要计算 ∑ i = L R f i   m o d   9999991 \sum_{i=L}^{R}f_{i}\ mod\ 9999991 i=LRfi mod 9999991

拉格朗日插值法

给定一个多项式
f ( x ) = a 0 + a 1 x + a 2 x 2 + ⋯ + a n x n f(x)=a_0+a_1x+a_2x^2+\cdots+a_nx^n f(x)=a0+a1x+a2x2++anxn
多项式的度数为 n n n,一共有 n + 1 n+1 n+1
对于这个函数,若已知其任意 n + 1 n+1 n+1 x x x取值以及其所对应的 y y y的值的话,可以利用拉格朗日插值来求得任意其他 x x x的值,即已知 ( x 0 , y 0 ) ) , ( x 1 , y 1 ) ) , ⋯   , ( x n , y n ) ) (x_0,y_0)),(x_1,y_1)),\cdots,(x_n,y_n)) (x0,y0)),(x1,y1)),,(xn,yn)),得
f ( x ) = L ( x ) = ∑ i = 0 n y i l i ( x ) = ∑ i = 0 n y i ∏ j ≠ i x − x j x i − x j f(x)=L(x)=\sum_{i=0}^ny_il_i(x)=\sum_{i=0}^ny_i\prod_{j\neq i}\frac{x-x_j}{x_i-x_j} f(x)=L(x)=i=0nyili(x)=i=0nyij̸=ixixjxxj
举个例子
( 0 , 1 ) , ( 1 , 4 ) ( 2 , 9 ) (0,1),(1,4)(2,9) (0,1),(1,4)(2,9)
f ( 0 ) = 1 f(0)=1 f(0)=1
f ( 1 ) = 4 f(1)=4 f(1)=4
f ( 2 ) = 9 f(2)=9 f(2)=9
那么
l 0 ( x ) = x − 1 0 − 1 ⋅ x − 2 0 − 2 l_0(x)=\frac{x-1}{0-1}\cdot \frac{x-2}{0-2} l0(x)=01x102x2
l 1 ( x ) = x − 0 1 − 0 ⋅ x − 2 1 − 2 l_1(x)=\frac{x-0}{1-0}\cdot \frac{x-2}{1-2} l1(x)=10x012x2
l 2 ( x ) = x − 0 2 − 0 ⋅ x − 1 2 − 1 l_2(x)=\frac{x-0}{2-0}\cdot \frac{x-1}{2-1} l2(x)=20x021x1
f ( x ) = L ( x ) = f ( 0 ) ⋅ l 0 ( x ) + f ( 1 ) ⋅ l 1 ( x ) + f ( 2 ) ⋅ l 2 ( x ) f(x)=L(x)=f(0)\cdot l_0(x)+f(1)\cdot l_1(x)+f(2)\cdot l_2(x) f(x)=L(x)=f(0)l0(x)+f(1)l1(x)+f(2)l2(x)
= 1 ⋅ x − 1 0 − 1 ⋅ x − 2 0 − 2 + 4 ⋅ x − 0 1 − 0 ⋅ x − 2 1 − 2 + 9 ⋅ x − 0 2 − 0 ⋅ x − 1 2 − 1 =1\cdot\frac{x-1}{0-1}\cdot \frac{x-2}{0-2} +4\cdot \frac{x-0}{1-0}\cdot \frac{x-2}{1-2}+9\cdot \frac{x-0}{2-0}\cdot \frac{x-1}{2-1} =101x102x2+410x012x2+920x021x1
= 1 ⋅ x − 1 0 − 1 ⋅ x − 2 0 − 2 + 4 ⋅ x − 0 1 − 0 ⋅ x − 2 1 − 2 + 9 ⋅ x − 0 2 − 0 ⋅ x − 1 2 − 1 =1\cdot\frac{x-1}{0-1}\cdot \frac{x-2}{0-2} +4\cdot \frac{x-0}{1-0}\cdot \frac{x-2}{1-2}+9\cdot \frac{x-0}{2-0}\cdot \frac{x-1}{2-1} =101x102x2+410x012x2+920x021x1
= 1 + 2 x + x 2 =1+2x+x^2 =1+2x+x2
若题目中给的 x x x是任意的话只能用 O ( n 2 ) O(n^2) O(n2)处理了

特殊情况

若x是连续的那么复杂度可以达到O(n),例如x的取值是从 0 ∼ n 0\sim n 0n,那么根据公式
l 0 ( x ) = ( x − 1 ) ( x − 2 ) ⋯ ( x − n ) ( 0 − 1 ) ( 0 − 2 ) ⋯ ( 0 − n ) l_0(x)=\frac{(x-1)(x-2)\cdots(x-n)}{(0-1)(0-2)\cdots(0-n)} l0(x)=(01)(02)(0n)(x1)(x2)(xn)
l 1 ( x ) = ( x − 0 ) ( x − 2 ) ⋯ ( x − n ) ( 1 − 0 ) ( 1 − 2 ) ⋯ ( 1 − n ) l_1(x)=\frac{(x-0)(x-2)\cdots(x-n)}{(1-0)(1-2)\cdots(1-n)} l1(x)=(10)(12)(1n)(x0)(x2)(xn)
⋮ \vdots
l n ( x ) = ( x − 0 ) ( x − 1 ) ⋯ ( x − ( n − 1 ) ) ( n − 0 ) ( n − 1 ) ⋯ ( n − ( n − 1 ) ) l_n(x)=\frac{(x-0)(x-1)\cdots(x-(n-1))}{(n-0)(n-1)\cdots(n-(n-1))} ln(x)=(n0)(n1)(n(n1))(x0)(x1)(x(n1))
我们先处理分子
对于所要求的 x x x我们可以预处理出
p = ( x − 0 ) ( x − 1 ) ( x − 2 ) ⋯ ( x − n ) p=(x-0)(x-1)(x-2)\cdots(x-n) p=(x0)(x1)(x2)(xn)
那么 l i ( x ) l_i(x) li(x)的分子部分可以用 p ( x − i ) \frac{p}{(x-i)} (xi)p求得,可以用费马小定理求得 ( x − i ) (x-i) (xi)的逆元
我们再处理分母
可以观察到 l 0 ( x ) l_0(x) l0(x)的分母为 ( − 1 ) n n ! (-1)^nn! (1)nn!
l 1 ( x ) l_1(x) l1(x) 1 ! ( − 1 ) n − 1 ( n − 1 ) ! 1!(-1)^{n-1}(n-1)! 1!(1)n1(n1)!
l 2 ( x ) l_2(x) l2(x) 2 ! ( − 1 ) n − 2 ( n − 2 ) ! 2!(-1)^{n-2}(n-2)! 2!(1)n2(n2)!
l i ( x ) l_i(x) li(x)的分子
i ! ( − 1 ) n − i ( n − i ) ! i!(-1)^{n-i}(n-i)! i!(1)ni(ni)!
所以我们只用预处理出阶乘逆元然后判断当前分母的正负即可,复杂度就是变为了 O ( n ) O(n) O(n)

fac[0]=1;
    for(int i=1; i<N; i++)
        fac[i]=(fac[i-1])*i%mod;
    inv[N-1]=quickmod(fac[N-1],mod-2);//费马小定理求n!的逆元也就是1/n! 
    for(int i=N-2; i>=0; i--)
        inv[i]=(inv[i+1]*(i+1))%mod;//(n-1)!的逆元和n!的关系为1/n!*n

思路

该题给了 x x x 0 ∼ n 0\sim n 0n一共 n + 1 n+1 n+1个取值,那么就是拉格朗日插值法的特殊情形,但是他最后要求的是 ∑ i = L R f ( i ) \sum_{i=L}^Rf(i) i=LRf(i)
我们令 S ( x ) = ∑ i = 0 x f ( i ) S(x)=\sum_{i=0}^{x}f(i) S(x)=i=0xf(i)
那么答案就是 a n s = S ( R ) − S ( L − 1 ) ans=S(R)-S(L-1) ans=S(R)S(L1)
如果知道 ∑ i = 1 x i n \sum_{i=1}^xi^n i=1xin的和函数的度为 n + 1 n+1 n+1的话,那么就可以的出
S ( x ) S(x) S(x)也是一个度为 n + 1 n+1 n+1的多项式,那么若知道了 S ( n + 1 ) S(n+1) S(n+1)的话就可以用已知的所给的 f ( x ) f(x) f(x)的前缀和直接对和函数拉格朗日插值了,其实也容易就是用 f ( x ) f(x) f(x)对f(n+1)插个值,得到 f ( n + 1 ) f(n+1) f(n+1)再对这 n + 2 n+2 n+2个前缀和对 S ( R ) S(R) S(R)插个值和对 S ( L − 1 ) S(L-1) S(L1)插个值,然后一减就可以得到答案了
多组数据注意初始化和以及线性求逆元处理一下还能减少一个log

#include<bits/stdc++.h>
using namespace std;
const int mod=9999991;
const int N=1005;
long long a[N];
long long sum[N];
long long fac[N];
long long inv[N];
long long _inv[mod+5];
long long quickmod(long long a,long long b)
{
    long long ans=1;
    while(b)
    {
        if(b%2==1)
            ans=ans*a%mod;
        a=a*a%mod;
        b=b/2;
    }
    return ans;
}
int main()
{
    fac[0]=1;
    for(int i=1; i<N; i++)
        fac[i]=1ll*(fac[i-1])*i%mod;
    inv[N-1]=quickmod(fac[N-1],mod-2);
    for(int i=N-2; i>=0; i--)
        inv[i]=(1ll*inv[i+1]*(i+1))%mod;
    _inv[0]=_inv[1]=1;
    for(int i=2;i<mod+5;i++)
        _inv[i]=((mod-mod/i)*_inv[mod%i])%mod;
    int t;
    scanf("%d",&t);
    while(t--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        memset(a,0,sizeof(a));
        memset(sum,0,sizeof(sum));
        long long z=1;
        for(int i=0; i<=n; i++)
        {
            scanf("%d",&a[i]);
            a[i]=a[i]%mod;
            z=z*((n+1-i+mod)%mod)%mod;
            if(i!=0)
                sum[i]=(sum[i-1]+a[i])%mod;
            else
                sum[i]=a[i];
        }
        for(int i=0; i<=n; i++)
        {
            if((n-i)%2==1)
                a[n+1]=(a[n+1]-z%mod*_inv[n+1-i]%mod*inv[n-i]%mod*inv[i]%mod%mod*a[i]%mod+mod)%mod;
            else
                a[n+1]=(a[n+1]+z%mod*_inv[n+1-i]%mod*inv[n-i]%mod*inv[i]%mod%mod*a[i]%mod+mod)%mod;
        }
        sum[n+1]=(sum[n]+a[n+1])%mod;
        while(m--)
        {
            int l,r;
            scanf("%d%d",&l,&r);
            if(r<=n+1)
            {
                printf("%d\n",(sum[r]-sum[l-1]+mod)%mod);
                continue;
            }
            long long x=1,y=1;
            for(int i=0; i<=n+1; i++)
            {
                x=x*(r-i)%mod;
                y=y*(l-1-i)%mod;
            }
            long long ans=0;
            for(int i=0; i<=n+1; i++)
            {
                if((n+1-i)%2==1)
                    ans=(ans-x%mod*_inv[r-i]%mod*inv[n+1-i]%mod*inv[i]%mod%mod*sum[i]%mod+mod)%mod;
                else
                    ans=(ans+x%mod*_inv[r-i]%mod*inv[n+1-i]%mod*inv[i]%mod%mod*sum[i]%mod+mod)%mod;
            }
            if(l-1<=n+1)
                ans=(ans-sum[l-1]+mod)%mod;
            else
            {
                for(int i=0; i<=n+1; i++)
                {
                    if((n+1-i)%2==1)
                        ans=(ans+y%mod*_inv[l-1-i]%mod*inv[n+1-i]%mod*inv[i]%mod%mod*sum[i]%mod+mod)%mod;
                    else
                        ans=(ans-y%mod*_inv[l-1-i]%mod*inv[n+1-i]%mod*inv[i]%mod%mod*sum[i]%mod+mod)%mod;
                }
            }
            printf("%lld\n",ans);
        }
    }
    return 0;
}
/*
1
3 2
1 10 49 142
6 7
95000 100000
*/



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值