例如:
∑i=1ni=n(n+1)2 ∑ i = 1 n i = n ( n + 1 ) 2
只需取三个点 (1,1),(2,3),(3,6) ( 1 , 1 ) , ( 2 , 3 ) , ( 3 , 6 ) 即可确定唯一方程。
同理 ∑i=1ni2 ∑ i = 1 n i 2 只需由四个点确定,但可以取三个点来逼近。
作用?
当n很大时,直接带入求出的函数求解。
对于 n+1 n + 1 个点对 (x0,y0),(x1,y1)...(xn,yn) ( x 0 , y 0 ) , ( x 1 , y 1 ) . . . ( x n , y n ) ,求一个函数 fi f i ,使得该函数在 xi x i 处取得对应 yi y i 值,而在其他 xj x j 取得 0 0 ,最后把这几个函数线性结合即可。可得:
求原函数的复杂度为 O(n2) O ( n 2 ) ,单次求值的复杂度为 O(n2) O ( n 2 ) 。
考虑得到的函数:
不难发现每个 fi f i 计算了重复部分。
设
则:
求原函数复杂度为 O(n2) O ( n 2 ) ,单次求值复杂度 O(n2) O ( n 2 ) 。
动态加点?
每次增加一个点,一般求法会重新计算一次达到 O(n2) O ( n 2 ) 的复杂度,而重心拉格朗日插值法只需 O(n) O ( n ) 计算 wi w i 即可。
横坐标连续?
不需多次计算 wi w i ,单次求值 O(n) O ( n ) ,预处理逆元的话求原函数也只会 O(n) O ( n ) 。
求
很显然是一个 n n 次多项式,对于每一个 x x 计算 n+1 n + 1 个点值确定函数,然后带入 Ux U x 计算即可,因为 xi x i 连续,所以时间复杂度 O(n2) O ( n 2 ) 。
听说结果是次数为 2n 2 n 的多项式,那么先DP出小的点,直接上拉格朗日就行了。
求
虽然看上去很复杂,其实拆开并不复杂。
首先:
其次:
可以对 g(m) g ( m ) 进行插值求大的 m m 数。
最后:
而且得出了两个结论:
1.每个求和号一般会增加多项式次数1.
2.插值可以嵌套。
特别是第二点,基本上可以在 O(k2) O ( k 2 ) 解决所有跟多项式有关的题。
Code for bzoj3453:
#include
using namespace std;
struct IO
{
streambuf *ib,*ob;
int buf[50];
inline void init()
{
ios::sync_with_stdio(false);
cin.tie(NULL);cout.tie(NULL);
ib=cin.rdbuf();ob=cout.rdbuf();
}
inline int read()
{
char ch=ib->sbumpc();int i=0,f=1;
while(!isdigit(ch)){if(ch=='-')f=-1;ch=ib->sbumpc();}
while(isdigit(ch)){i=(i<<1)+(i<<3)+ch-'0';ch=ib->sbumpc();}
return i*f;
}
inline void W(int x)
{
if(!x){ob->sputc('0');return;}
if(x<0){ob->sputc('-');x=-x;}
while(x){buf[++buf[0]]=x%10,x/=10;}
while(buf[0])ob->sputc(buf[buf[0]--]+'0');
}
}io;
const int mod=1234567891,lim=130;
int k,a,n,d;
int fg[150],fy[150],wf[150],fac[150],inv[150];
inline int power(int a,int b)
{
int res=1;
for(;b;b>>=1)
{
if(b&1)res=1ll*res*a%mod;
a=1ll*a*a%mod;
}
return res;
}
int DIV;
inline int calc(int *w,int u)
{
if(u<=lim)return w[u];
static int l,res,Div;
l=1,res=0,Div=DIV;
for(int i=1;i<=lim;i++)l=1ll*l*((1ll*u-i+mod)%mod)%mod;
for(int i=1;i<=lim;i++)
{
res=(1ll*res+1ll*Div*power(((1ll*u-i+mod)%mod),mod-2)%mod*w[i]%mod)%mod;
Div=1ll*Div*(i-lim+mod)%mod*inv[i]%mod;
}
return 1ll*res*l%mod;
}
int main()
{
io.init();int T=io.read();
fac[0]=fac[1]=inv[0]=inv[1]=1;DIV=1;
for(int i=2;i<=lim;i++)inv[i]=(-1ll*(mod/i)*inv[mod%i]%mod+mod)%mod;
for(int i=1;i<=lim;i++)fac[i]=1ll*fac[i-1]*i%mod;
for(int i=2;i<=lim;i++)DIV=1ll*DIV*power(1-i+mod,mod-2)%mod;
while(T--)
{
k=io.read(),a=io.read(),n=io.read(),d=io.read();
for(int i=1;i<=lim;i++){fg[i]=power(i,k);}
for(int i=1;i<=lim;i++){fg[i]=(1ll*fg[i]+fg[i-1])%mod;}
for(int i=1;i<=lim;i++){fg[i]=(1ll*fg[i]+fg[i-1])%mod;}
fy[0]=calc(fg,a);
for(int i=1;i<=lim;i++)
{
fy[i]=(1ll*fy[i-1]+calc(fg,(1ll*a+1ll*i*d)%mod))%mod;
}
printf("%d\n",calc(fy,n));
}
}