定义了 ,数字可能会非常大,所以对9999991取模。对于一个多项式,XH不知道任意一个 ,但是他知道 的值,他想要计算
题意
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 有举例说明
数学上的知识,理解后实际上要求的是一个公式,分子是前缀积乘后缀积,分母是两个阶乘的逆元。
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;
}