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

题目链接

定义了   f_{(x)}=a_{0}+a_{1} x^{1}+a_{2} x^{2}+\cdots+a_{n} x^{n},数字可能会非常大,所以对9999991取模。对于一个多项式,XH不知道任意一个 a_i  ,但是他知道  f_{(0)}, f_{(1)}, f_{(2)} \cdots f_{(n)} 的值,他想要计算

\sum\limits_{i=L}^Rf _{i} \ mod\9999991 $

题意

f(x)是n次多项式,有n+1个未知数,已知n+1个方程,所以可以f(x)是确定的。求Σf(x), x从L到R。

拉格朗日插值法

https://www.luogu.org/blog/attack/solution-p4781 分类:一般插值法;x取值连续;重心拉格朗日插值法

https://blog.csdn.net/Code92007/article/details/94412729 公式推导 and 一般的拉格朗日插值法

https://blog.csdn.net/ftx456789/article/details/90750508 有举例说明

f(x)=L(x)=\sum\limits_{i=0}^ny _{i} l_{i} (x) = \sum\limits_{i=0}^ny _{i}\prod \limits_{j\not=i }\frac{x-x_i}{x_i-x_j}

数学上的知识,理解后实际上要求的是一个公式,分子是前缀积乘后缀积,分母是两个阶乘的逆元。

fact[i]是 阶乘 i ! ,finv[i] 是 阶乘 i !的逆元,a[]是原来函数的函数值,sum是新的函数的函数值(sum的函数是原来f函数的前缀和)。

pre[],suf[] 分别是求分子时候的前缀积、后缀积。分子这里如果用乘法逆元算的话,时间复杂度应该是O(nlogn),n次求逆元,逆元用快速幂是logn。用前缀积、后缀积过一遍只需要O(n)。


#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int mod = 9999991;
const int N = 1010;
int a[N],sum[N],pre[N],suf[N];
int fact[N],finv[N];

int quickpow(int a,int b,int n)
{
    int res = 1;
    while(b){
        if(b&1) res = 1ll*res*a%n;
        a = 1ll*a*a%n;
        b >>=1;
    }
    return res;
}
//fact阶乘,finv阶乘的逆元
void init()
{
    fact[0] = 1;
    for(int i=1;i<N;i++)
        fact[i] = 1ll*fact[i-1]*i%mod;
    finv[N-1] = quickpow(fact[N-1],mod-2,mod);
    for(int i=N-1;i>=1;i--)
        finv[i-1] = 1ll*finv[i]*i%mod;
}
//已知数组f[0]到f[n],求f[x].  pre前缀积,suf后缀积
int cal(int *f,int n,int x)
{
    if(x<=n) return f[x];
    int ans = 0;
    pre[0] = suf[n] = 1;
    for(int i=1;i<=n;i++)
        pre[i] = 1ll*pre[i-1]*(x-(i-1))%mod;
    for(int i=n-1;i>=0;i--)
        suf[i] = 1ll*suf[i+1]*(x-(i+1))%mod;

    for(int i=0;i<=n;i++){
        int fu = (n-i)&1?-1:1;
        ans += 1ll*f[i]*fu*pre[i]%mod * suf[i]%mod * finv[i]%mod * finv[n-i]%mod;
        ans = (ans%mod + mod)%mod;
        //printf("%d !!!\n",ans);
    }
    return ans;
}

int main()
{
    init();
    int T; scanf("%d",&T);
    while(T--)
    {
        int m,n; scanf("%d%d",&n,&m);
        for(int i=0;i<=n;i++)
            scanf("%d",&a[i]);
        a[n+1] = cal(a,n,n+1);
        sum[0] = a[0];
        for(int i=1;i<=n+1;i++)
            sum[i] = (sum[i-1] + a[i])%mod;
        while(m--){
            int l,r; scanf("%d%d",&l,&r);
            int ans = cal(sum,n+1,r) - cal(sum,n+1,l-1);
            ans = (ans%mod + mod)%mod;
            printf("%d\n",ans);
        }
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值