基本定理
我们中学就知道:两点确定一直线。对于这一点的拓展:n点可以确定n-1阶的多项式。
关于证明,可以联想待定系数法:
设多项式为
f
(
x
)
=
a
0
+
a
1
x
n
+
x
2
x
2
+
…
…
+
a
n
1
x
n
−
1
f(x)=a_0+a_1x^n+x_2x^2+……+a_{n_1}x^{n-1}
f(x)=a0+a1xn+x2x2+……+an1xn−1
由此可知,我们只需要n个独立方程就能确定此多项式。
那么,假设说我们要求这个多项式的某点的取值
f
(
k
)
f(k)
f(k),最直接的想法是确定待定系数,再来求
f
(
k
)
f(k)
f(k)。也就是解方程组:
{
a
0
+
a
1
x
1
+
a
2
x
1
n
+
…
…
a
n
x
1
n
=
b
1
a
0
+
a
1
x
2
+
a
2
x
2
n
+
…
…
a
n
x
2
n
=
b
2
…
…
a
0
+
a
1
x
n
+
a
2
x
n
n
+
…
…
a
n
x
n
n
=
b
3
\begin{cases} a_0+a_1x_1+a_2x_1^n+……a_nx_1^n=b_1\\ a_0+a_1x_2+a_2x_2^n+……a_nx_2^n=b_2\\ ……\\ a_0+a_1x_n+a_2x_n^n+……a_nx_n^n=b_3 \end{cases}
⎩⎪⎪⎪⎨⎪⎪⎪⎧a0+a1x1+a2x1n+……anx1n=b1a0+a1x2+a2x2n+……anx2n=b2……a0+a1xn+a2xnn+……anxnn=b3
写成算法就是用高斯消元,复杂度为
O
(
n
3
)
O(n^3)
O(n3)
那么如果只求某个点的取值呢?拉格朗日大佬给我们一个新的方法:
f
(
k
)
=
∑
i
=
1
n
(
y
i
∏
j
≠
i
k
−
x
j
x
i
−
x
j
)
f(k)=\sum_{i=1}^n(y_i\prod_{j\neq i}{k-x_j\over x_i-x_j})
f(k)=i=1∑n(yij=i∏xi−xjk−xj)
证明可以看下oiwiki上的,类似于crt那样构造出解的方法。
这个解法的复杂度为
O
(
n
2
)
O(n^2)
O(n2),有些情况下还能继续优化。
模板题:https://www.luogu.com.cn/problem/P4781
直接套式子写就行了。
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long ull;
const int mod=998244353;
typedef long long ll;
template<typename T>
void read(T&x){
x=0;
int f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f*=-1;
ch=getchar();
}
while(isdigit(ch)){
x=x*10+(ch-'0');
ch=getchar();
}x*=f;
}
template<typename T>
void write(T x){
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);
putchar(x%10+'0');
}
//===============================================
const int maxn=2e3+10;
int x[maxn],y[maxn];
int n,k;
ll ksm(ll a,ll n){
ll res=1;
while(n){
if(n&1)res=res*a%mod;
a=a*a%mod;
n>>=1;
}
return res;
}
int solve(int x[],int y[],int n,int k){
int res=0;
for(int i=1;i<=n;++i){
int up=y[i],down=1;
for(int j=1;j<=n;++j){
if(i==j)continue;
up=1ll*up*(k-x[j])%mod;up=(up%mod+mod)%mod;
down=1ll*down*(x[i]-x[j])%mod;down=(down%mod+mod)%mod;
}
res=(res+1ll*up*ksm(down,mod-2)%mod)%mod;
res=(res%mod+mod)%mod;
}
return res;
}
int main(){
//freopen("in.txt","r",stdin);
read(n),read(k);
for(int i=1;i<=n;++i){
read(x[i]);read(y[i]);
}
cout<<solve(x,y,n,k)<<endl;
return 0;
}
当x取值为连续时
观察下原式:
f
(
k
)
=
∑
i
=
1
n
(
y
i
∏
j
≠
i
k
−
x
j
x
i
−
x
j
)
f(k)=\sum_{i=1}^n(y_i\prod_{j\neq i}{k-x_j\over x_i-x_j})
f(k)=i=1∑n(yij=i∏xi−xjk−xj)
如果x的取值为0,1,2,3,……,n-1,那么,分子和分母就都是阶乘形式了(可以找个样例手写下),那么,我们就可以我们可以预处理分子分母后,
O
(
n
)
O(n)
O(n)构造解。具体可以看代码。
模板题:https://nanti.jisuanke.com/t/40254
#include <bits/stdc++.h>
using namespace std;
template<typename T>
void read(T&x){
x=0;
int f=1;
char ch=getchar();
while(!isdigit(ch)){
if(ch=='-')f*=-1;
ch=getchar();
}
while(isdigit(ch)){
x=x*10+(ch-'0');
ch=getchar();
}x*=f;
}
template<typename T>
void write(T x){
if(x<0)putchar('-'),x=-x;
if(x>9)write(x/10);
putchar(x%10+'0');
}
//=========================================================
typedef long long ll;
#define int ll
const int mod=9999991;
const int maxn=1e3+10;
int t,n,m;
int y[maxn];
int f[maxn],finv[maxn];
inline int sub(int x,int y){
return x-y>=0?x-y:x-y+mod;
}
inline int add(int x,int y){
return x+y<mod?x+y:x+y-mod;
}
inline ll mul(int x,int y){
return (1ll*x*y%mod+mod)%mod;
}
inline int ksm(int a,int n){
int res=1;
while(n){
if(n&1)res=1ll*res*a%mod;
a=1ll*a*a%mod;
n>>=1;
}
return res;
}
inline void init(){
f[0]=1;
for(int i=1;i<maxn;++i)f[i]=1ll*f[i-1]*i%mod;
finv[maxn-1]=ksm(f[maxn-1],mod-2);
for(int i=maxn-2;i>=0;--i){
finv[i]=1ll*finv[i+1]*(i+1)%mod;
}
}
int pre[maxn],suf[maxn];
int calc(int y[],int k,int n){
if(k<=n)return y[k];
pre[0]=1;suf[n]=1;
//预处理分子
for(int i=1;i<=n;++i)pre[i]=1ll*pre[i-1]*(k-(i-1))%mod;
for(int i=n-1;i>=0;--i)suf[i]=1ll*suf[i+1]*(k-(i+1))%mod;
int res=0;
for(int i=0;i<=n;++i){
//int up=mul(y[i],mul(pre[i],suf[i]));
//int down=mul(finv[i],finv[n-i]);
//int fu=((n-i&1)?-1:1);
//res=(res+1ll*fu*res*pre[i]%mod*suf[i]%mod*finv[i]%mod*finv[n-i]%mod);
//res=(res%mod+mod)%mod;
//res=(res+((fu*up)%mod*down)%mod)%mod;
//res=(res%mod+mod)%mod;
int fu=(n-i)&1?-1:1;
res +=1ll*y[i]*fu*pre[i]%mod * suf[i]%mod * finv[i]%mod * finv[n-i]%mod;
res=(res%mod+mod)%mod;
//if(!flag)cerr<<f[i]<<" "<<fu<<" "<<pre[i]<<" "<<suf[i]<<" "<<finv[i]<<" "<<finv[n-i]<<endl;
}
return res;
}
inline void prelude(){
y[n+1]=calc(y,n+1,n);
//cerr<<"**"<<y[n+1]<<endl;
for(int i=1;i<=n+1;++i)y[i]=add(y[i-1],y[i]);
//y[n+1]=add(y[n+1],y[n]);
}
signed main(){
//freopen("in.txt","r",stdin);
//freopen("out.txt","w",stdout);
init();
read(t);
while(t--){
read(n),read(m);
for(int i=0;i<=n;++i)read(y[i]);
prelude();//预处理前缀和
//cerr<<y[n+1]<<endl;
while(m--){
int L,R;
read(L),read(R);
write(sub(calc(y,R,n+1),calc(y,L-1,n+1)));//差分求部分和
putchar('\n');
}
}
return 0;
}
练习题:
https://www.luogu.com.cn/problem/P4593
https://ac.nowcoder.com/acm/problem/218514