[xsy2781]快速插值

Posted jefflyy

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[xsy2781]快速插值相关的知识,希望对你有一定的参考价值。

被神秘力量驱使去学这个东西...

由$(x_i,y_i)(0leq ileq n)$构造多项式$L(x)=sumlimits_{i=0}^ny_iprodlimits_{substack{0leq jleq n\i e j}}frac{x-x_j}{x_i-x_j}$

观察$L(x_k)$里的$i,j$,如果$i=k$,那么右边的大pi每一项的分子分母都相同,值为$1$,否则右边的大pi在$j=k$时存在一个分子为$0$,这个多项式对$forall0leq ileq n$满足$L(x_i)=y_i$

大pi中,分母是常数,所以我们要对每个$i$求出$prodlimits_{substack{0leq jleq n\i e j}}(x_i-x_j)$,直接对$frac{mathrm d}{mathrm dx}prodlimits_{i=0}^n(x-x_i)$在所有$x_i$处多点求值即可

分子用分治求就可以了,合并的时候(左边的答案乘右边的$prod(x-x_i)$)加上(左边的$prod(x-x_i)$乘右边的答案)即可,递归到底层返回预处理好的$dfrac{y_i}{prodlimits_{substack{0leq jleq n\i e j}}(x_i-x_j)}$就可以了

总时间复杂度$O(nlog_2^2n)$,空间复杂度$O(nlog_2n)$,常数巨大,我写得太丑了,$n=50000$要跑$3$秒多

#include<stdio.h>
#include<string.h>
typedef long long ll;
const int mod=998244353,maxn=131072;
void swap(int&a,int&b){
	int c=a;
	a=b;
	b=c;
}
int max(int a,int b){return a>b?a:b;}
int mul(int a,int b){return a*(ll)b%mod;}
int ad(int a,int b){return(a+b)%mod;}
int de(int a,int b){return(a-b)%mod;}
int pow(int a,int b){
	int s=1;
	while(b){
		if(b&1)s=mul(s,a);
		a=mul(a,a);
		b>>=1;
	}
	return s;
}
int rev[maxn],N,iN;
void pre(int n){
	int i,k;
	for(N=1,k=0;N<n;N<<=1)k++;
	for(i=0;i<N;i++)rev[i]=(rev[i>>1]>>1)|((i&1)<<(k-1));
	iN=pow(N,mod-2);
}
void ntt(int*a,int on){
	int i,j,k,t,w,wn;
	for(i=0;i<N;i++){
		if(i<rev[i])swap(a[i],a[rev[i]]);
	}
	for(i=2;i<=N;i<<=1){
		wn=pow(3,(on==1)?(mod-1)/i:(mod-1-(mod-1)/i));
		for(j=0;j<N;j+=i){
			w=1;
			for(k=0;k<i>>1;k++){
				t=mul(w,a[i/2+j+k]);
				a[i/2+j+k]=de(a[j+k],t);
				a[j+k]=ad(a[j+k],t);
				w=mul(w,wn);
			}
		}
	}
	if(on==-1){
		for(i=0;i<N;i++)a[i]=mul(a[i],iN);
	}
}
int t0[maxn];
void getinv(int*a,int*b,int n){
	if(n==1){
		b[0]=pow(a[0],mod-2);
		return;
	}
	int i;
	getinv(a,b,n>>1);
	pre(n<<1);
	memset(t0,0,N<<2);
	memcpy(t0,a,n<<2);
	ntt(t0,1);
	ntt(b,1);
	for(i=0;i<N;i++)b[i]=mul(b[i],2-mul(t0[i],b[i]));
	ntt(b,-1);
	for(i=n;i<N;i++)b[i]=0;
}
int ta[maxn],tb[maxn],tc[maxn];
#define clr while(k!=0&&tc[k]==0)k--;			memcpy(c,tc,(k+1)<<2);
void add(int*a,int n,int*b,int m,int*c,int&k){
	k=max(n,m);
	memset(ta,0,(k+1)<<2);
	memcpy(ta,a,(n+1)<<2);
	memset(tb,0,(k+1)<<2);
	memcpy(tb,b,(m+1)<<2);
	for(int i=0;i<=k;i++)tc[i]=ad(ta[i],tb[i]);
	clr
}
void dec(int*a,int n,int*b,int m,int*c,int&k){
	k=max(n,m);
	memset(ta,0,(k+1)<<2);
	memcpy(ta,a,(n+1)<<2);
	memset(tb,0,(k+1)<<2);
	memcpy(tb,b,(m+1)<<2);
	for(int i=0;i<=k;i++)tc[i]=de(ta[i],tb[i]);
	clr
}
void dif(int*a,int n,int*c,int&k){
	k=n-1;
	for(int i=1;i<=n;i++)c[i-1]=mul(i,a[i]);
}
void reverse(int*a,int n){
	for(int i=0;i<=n>>1;i++)swap(a[i],a[n-i]);
}
void mul(int*a,int n,int*b,int m,int*c,int&k){
	int i;
	k=n+m;
	pre(k+1);
	memset(ta,0,N<<2);
	memcpy(ta,a,(n+1)<<2);
	memset(tb,0,N<<2);
	memcpy(tb,b,(m+1)<<2);
	ntt(ta,1);
	ntt(tb,1);
	for(i=0;i<N;i++)tc[i]=mul(ta[i],tb[i]);
	ntt(tc,-1);
	clr
}
int t1[maxn];
void div(int*a,int n,int*b,int m,int*c,int&k){
	if(n<m){
		k=0;
		return;
	}
	int i,rn;
	for(rn=1;rn<n-m+1;rn<<=1);
	memset(ta,0,rn<<3);
	memcpy(ta,a,(n+1)<<2);
	memset(tb,0,rn<<3);
	memcpy(tb,b,(m+1)<<2);
	reverse(tb,m);
	for(i=rn;i<=m;i++)tb[i]=0;
	memset(t1,0,rn<<3);
	getinv(tb,t1,rn);
	pre(rn<<1);
	reverse(ta,n);
	for(i=rn;i<=n;i++)ta[i]=0;
	ntt(ta,1);
	ntt(t1,1);
	for(i=0;i<N;i++)tc[i]=mul(ta[i],t1[i]);
	ntt(tc,-1);
	k=n-m;
	reverse(tc,k);
	clr
}
void modulo(int*a,int n,int*b,int m,int*c,int&k){
	if(n<m){
		k=n;
		memcpy(c,a,(n+1)<<2);
		return;
	}
	div(a,n,b,m,t1,k);
	mul(t1,k,b,m,t1,k);
	dec(a,n,t1,k,c,k);
}
int X[50010],Y[50010],*tr[200010],go;
void build(int l,int r,int x){
	if(l==r){
		tr[x]=new int[2];
		tr[x][1]=1;
		tr[x][0]=-X[l];
		return;
	}
	int mid=(l+r)>>1;
	build(l,mid,x<<1);
	build(mid+1,r,x<<1|1);
	tr[x]=new int[r-l+2];
	mul(tr[x<<1],mid-l+1,tr[x<<1|1],r-mid,tr[x],go);
}
void solve(int*f,int n,int l,int r,int x,int*ans){
	int mid=(l+r)>>1,*now;
	now=new int[r-l+1];
	modulo(f,n,tr[x],r-l+1,now,n);
	if(l==r){
		ans[l]=now[0];
		return;
	}
	solve(now,n,l,mid,x<<1,ans);
	solve(now,n,mid+1,r,x<<1|1,ans);
}
int di[50010],res[50010];
int*solve(int l,int r,int x){
	int mid=(l+r)>>1,*res,n1,n2,*t1,*t2;
	res=new int[r-l+1];
	if(l==r){
		res[0]=Y[l];
		return res;
	}
	t1=new int[r-mid+1];
	mul(solve(l,mid,x<<1),mid-l,tr[x<<1|1],r-mid,t1,n1);
	t2=new int[r-mid+1];
	mul(tr[x<<1],mid-l+1,solve(mid+1,r,x<<1|1),r-mid-1,t2,n2);
	add(t1,n1,t2,n2,res,go);
	return res;
}
int main(){
	int n,i,*ans;
	scanf("%d",&n);
	for(i=0;i<=n;i++)scanf("%d%d",X+i,Y+i);
	build(0,n,1);
	dif(tr[1],n+1,di,i);
	solve(di,n,0,n,1,res);
	for(i=0;i<=n;i++)Y[i]=mul(Y[i],pow(res[i],mod-2));
	ans=solve(0,n,1);
	for(i=0;i<=n;i++)printf("%d ",ad(ans[i],mod));
}

以上是关于[xsy2781]快速插值的主要内容,如果未能解决你的问题,请参考以下文章

XSY3309Dreamweaver 高斯消元 拉格朗日插值

c_cpp 这个简单的代码片段显示了如何使用有符号整数在C中完成插值。 for()循环确定要插入的范围

如何在片段着色器中找到 4 个顶点之间的插值位置?

光栅化插值方法

@算法 - 4@ 多项式的多点求值与快速插值

如何使用插值来设置属性选择器?