题解 多项式快速插值

Posted dark-romance

tags:

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

题目传送门

题目大意

给出(n)个点((x_i,y_i)),求出经过这(n)个点的一个(n-1)次多项式。

(nle 10^5)

思路

差点卡常死在这里。论多项式里面的调参(有( exttt{SA})内味了)(雾

我们发现这个东西我们显然可以使用拉格朗日插值法,我们可以求到答案为:

[sum_{i=1}^{n} y_iprod_{j=1wedge i ot= j} frac{x-x_j}{x_i-x_j} ]

变一下我们就可以得到:

[sum_{i=1}^{n} frac{y_i}{prod_{j=1wedge i ot= j}^{n}(x_i-x_j)}prod_{j=1wedge i ot= j}^{n} (x-x_j) ]

我们假设(g(x)=prod_{i=1}^{n}(x-x_i)),那么上面的式子就是:

[sum_{i=1}^{n} frac{y_i}{frac{g(x_i)}{x_i-x_i}}prod_{j=1wedge i ot= j}^{n}(x-x_j) ]

我们发现(frac{g(x_i)}{x_i-x_i})可以使用洛必达法则求到,答案即为(g^{‘}(x_i))

所以式子就是:

[sum_{i=1}^{n} frac{y_i}{g^{‘}(x_i)} prod_{j=1wedge i ot= j}^{n} (x-x_j) ]

我们发现这个式子如果我们直接求的话会炸成(Theta(n^2log^2n)),但是我们发现可以使用分治解决这个问题。

我们假设当前区间为(l,r),答案为(f_{l,r}),中点为(mid),那么可以得到:

[f_{l,r}=sum_{i=l}^{r} (frac{y_i}{g^{‘}(x_i)}prod_{j=lwedge i ot= j}^{r}(x-x_j)) ]

我们从中间拆开就可以得到:

[f_{l,r}=prod_{k=mid+1}^{r}(x-x_k)(sum_{i=l}^{mid} frac{y_i}{g^{‘}(x_i)}prod_{j=lwedge i ot=j}^{mid}(x-x_j))+prod_{k=l}^{mid}(x-x_k)(sum_{i=mid+1}^{r} frac{y_i}{g^{‘}(x_i)}prod_{j=mid+1wedge i ot=j}^{r}(x-x_j)) ]

即:

[f_{l,r}=prod_{k=mid+1}^{r} (x-x_k) f_{l,mid}+prod_{k=l}^{mid} (x-x_k)f_{mid+1,r} ]

于是我们就可以直接分治解决了。至于(g^{‘}(x_i))可以使用多项式多点求值(Theta(nlog ^2n))预处理求出来,每一层的(prod_{k=l}^{r}(x-x_k))也可以使用分治求出来。总时间复杂度根据主定理为(F(n)=F(n/2)+Theta(nlog n)=Theta(nlog^2n))

但是这道题有点卡常,所以多项式多点求值的地方需要调参,我差点自闭了。。。

( exttt{Code})

#include <bits/stdc++.h>
using namespace std;

#define SZ(x) ((int)x.size())
#define Int register int
#define mod 998244353
#define ll long long
#define MAXN 1000005

int mul (int a,int b){return 1ll * a * b % mod;}
int dec (int a,int b){return a >= b ? a - b : a + mod - b;}
int add (int a,int b){return a + b >= mod ? a + b - mod : a + b;}
int qkpow (int a,int k){
	int res = 1;for (;k;k >>= 1,a = 1ll * a * a % mod) if (k & 1) res = 1ll * res * a % mod;
	return res;
}
int inv (int x){return qkpow (x,mod - 2);}

typedef vector <int> poly;

int w[MAXN],rev[MAXN];

void init_ntt (){
	int lim = 1 << 18;
	for (Int i = 0;i < lim;++ i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << 17);
	int Wn = qkpow (3,(mod - 1) / lim);w[lim >> 1] = 1;
	for (Int i = lim / 2 + 1;i < lim;++ i) w[i] = mul (w[i - 1],Wn);
	for (Int i = lim / 2 - 1;i;-- i) w[i] = w[i << 1];
}

void ntt (poly &a,int lim,int type){
#define G 3
#define Gi 332748118
	static int d[MAXN];
	for (Int i = 0,z = 18 - __builtin_ctz(lim);i < lim;++ i) d[rev[i] >> z] = a[i];
	for (Int i = 1;i < lim;i <<= 1)
		for (Int j = 0;j < lim;j += i << 1)
			for (Int k = 0;k < i;++ k){
				int x = mul (w[i + k],d[i + j + k]);
				d[i + j + k] = dec (d[j + k],x),d[j + k] = add (d[j + k],x);
			}
	for (Int i = 0;i < lim;++ i) a[i] = d[i] % mod;
	if (type == -1){
		reverse (a.begin() + 1,a.begin() + lim);
		for (Int i = 0,Inv = inv (lim);i < lim;++ i) a[i] = mul (a[i],Inv);
	}
#undef G
#undef Gi 
}

poly operator + (poly a,poly b){
	a.resize (max (SZ (a),SZ (b)));
	for (Int i = 0;i < SZ (b);++ i) a[i] = add (a[i],b[i]);
	return a;
}

poly operator - (poly a,poly b){
	a.resize (max (SZ (a),SZ (b)));
	for (Int i = 0;i < SZ (b);++ i) a[i] = dec (a[i],b[i]);
	return a;
}

poly operator * (poly a,int b){
	for (Int i = 0;i < SZ (a);++ i) a[i] = mul (a[i],b);
	return a;
}

poly der (poly a){
	for (Int i = 0;i < SZ (a) - 1;++ i) a[i] = mul (a[i + 1],i + 1);
	a.pop_back ();return a;
}

poly operator * (poly a,poly b){
	int d = SZ (a) + SZ (b) - 1,lim = 1;while (lim < d) lim <<= 1;
	a.resize (lim),b.resize (lim);
	ntt (a,lim,1),ntt (b,lim,1);
	for (Int i = 0;i < lim;++ i) a[i] = mul (a[i],b[i]);
	ntt (a,lim,-1),a.resize (d);
	return a;
}

poly inv (poly a,int n){
	poly b(1,inv (a[0])),c;
	for (Int l = 4;(l >> 2) < n;l <<= 1){
		c.resize (l >> 1);
		for (Int i = 0;i < (l >> 1);++ i) c[i] = i < n ? a[i] : 0;
		c.resize (l),b.resize (l);
		ntt (c,l,1),ntt (b,l,1);
		for (Int i = 0;i < l;++ i) b[i] = mul (b[i],dec (2,mul (b[i],c[i])));
		ntt (b,l,-1),b.resize (l >> 1);
	}
	b.resize (n);
	return b;
}

poly inv (poly a){return inv (a,SZ (a));}

poly Mod (poly F,poly G){
	int n = SZ (F) - 1,m = SZ (G) - 1;poly Q;Q.resize (m + 1);for (Int i = 0;i <= m;++ i) Q[i] = G[i];
	reverse (F.begin(),F.end()),reverse (G.begin(),G.end()),reverse (Q.begin(),Q.end()),Q.resize (n - m + 1),Q = inv (Q) * F,Q.resize (n - m + 1),reverse (Q.begin(),Q.end());
	reverse (F.begin(),F.end()),reverse (G.begin(),G.end()),Q = G * Q,Q.resize (m),Q = F - Q,Q.resize (m);
	return Q;
}

template <typename T> inline void read (T &t){t = 0;char c = getchar();int f = 1;while (c < ‘0‘ || c > ‘9‘){if (c == ‘-‘) f = -f;c = getchar();}while (c >= ‘0‘ && c <= ‘9‘){t = (t << 3) + (t << 1) + c - ‘0‘;c = getchar();} t *= f;}
template <typename T,typename ... Args> inline void read (T &t,Args&... args){read (t);read (args...);}
template <typename T> inline void write (T x){if (x < 0){x = -x;putchar (‘-‘);}if (x > 9) write (x / 10);putchar (x % 10 + ‘0‘);}

int n,a[MAXN],bb[MAXN],AAns[MAXN];
poly AAA,D[MAXN << 2],DR[MAXN << 2];

void divide1 (int i,int l,int r){
	if (l == r) return DR[i].resize (2),DR[i][0] = mod - a[l],DR[i][1] = 1,void ();
	int mid = (l + r) >> 1;divide1 (i << 1,l,mid),divide1 (i << 1 | 1,mid + 1,r);
	DR[i] = DR[i << 1] * DR[i << 1 | 1];
}

ll c1,c2,c3,c4,b[17];

void divide2 (int i,int l,int r,poly AA){
	if (r - l <= 512){
		for (Int i = l;i <= r;++ i){
			int x = a[i];b[0] = 1;int now = r - l;
			for (Int j = 1;j <= 16;++ j) b[j] = b[j - 1] * x % mod;
			ll Ans = AA[now];
			for (Int j = now - 1;j - 15 >= 0;j -= 16){
				c1 = Ans * b[16] + AA[j] * b[15] + AA[j - 1] * b[14] + AA[j - 2] * b[13],c1 %= mod;
				c2 = AA[j - 3] * b[12] + AA[j - 4] * b[11] + AA[j - 5] * b[10] + AA[j - 6] * b[9],c2 %= mod;
				c3 = AA[j - 7] * b[8] + AA[j - 8] * b[7] + AA[j - 9] * b[6] + AA[j - 10] * b[5],c3 %= mod;
				c4 = AA[j - 11] * b[4] + AA[j - 12] * b[3] + AA[j - 13] * b[2] + AA[j - 14] * b[1],c4 %= mod;
				Ans = (c1 + c2 + c3 + c4 + AA[j - 15]) % mod;
			} 
			for (Int j = now % 16 - 1;~j;-- j) Ans = (Ans * x + AA[j]) % mod;
			AAns[i] = Ans;
		}
		return ;
	}
	if (l == r) return write (AA[0]),putchar (‘
‘),void ();
	poly B = Mod (AA,DR[i << 1]);int mid = (l + r) >> 1;divide2 (i << 1,l,mid,B);B = Mod (AA,DR[i << 1 | 1]);divide2 (i << 1 | 1,mid + 1,r,B);
}

void divide3 (int i,int l,int r){
	if (l == r) return D[i].resize (1),D[i][0] = mul (bb[l],inv (AAns[l])),void ();
	int mid = (l + r) >> 1;divide3 (i << 1,l,mid),divide3 (i << 1 | 1,mid + 1,r);
	D[i] = DR[i << 1 | 1] * D[i << 1] + DR[i << 1] * D[i << 1 | 1];
}

signed main(){
	init_ntt(),read (n);
	for (Int i = 1;i <= n;++ i) read (a[i],bb[i]);
	divide1 (1,1,n),AAA = der (DR[1]),divide2 (1,1,n,AAA),divide3 (1,1,n);
	for (Int i = 0;i < n;++ i) write (D[1][i]),putchar (‘ ‘);putchar (‘
‘);
	return 0;
}

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

多项式快速插值

解题报告多项式求值与插值(拉格朗日插值)(ACM / OI)

拉格朗日插值

[xsy2781]快速插值

多点求值与快速插值

牛顿法、黄金分割法、二次插值法实验(最优化1)