Loading

【笔记】基础多项式

来自\(\texttt{SharpnessV}\)省选复习计划中的基础多项式


P3803 【模板】多项式乘法(FFT)

快速傅里叶变换\(\rm FFT\),时间复杂度\(\rm O(N\log N)\)

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 2100000
using namespace std;
struct node{
	double a,b; //a+bi
	node(double x=0,double y=0){a=x;b=y;}
}a[N],b[N];
const double pi = acos (-1) ;
node operator*(node x,node y){return node(x.a*y.a-x.b*y.b,x.a*y.b+x.b*y.a);}
node operator+(node x,node y){return node(x.a+y.a,x.b+y.b);}
node operator-(node x,node y){return node(x.a-y.a,x.b-y.b);}
int n,m,t,rev[N];
void fft(node *u,int op){
	rep(i,0,t-1)if(rev[i]>i)swap(u[rev[i]],u[i]);
	for(int l=1,k=2;k<=t;k<<=1,l<<=1){
		node cur(cos(pi/l),op*sin(pi/l));
		for(int i=0;i<t;i+=k){
			node now(1,0);
			rep(j,0,l-1){
				node x=u[i+j],y=now*u[i+j+l];
				u[i+j]=x+y;u[i+j+l]=x-y;
				now=now*cur;
			}
		}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	rep(i,0,n)scanf("%lf",&a[i].a);
	rep(i,0,m)scanf("%lf",&b[i].a);
	t=1;while(t<=n+m)t<<=1;
	rep(i,0,t-1)rev[i]=(rev[i>>1]>>1)|((i&1)?(t>>1):0);
	fft(a,1);fft(b,1);rep(i,0,t-1)a[i]=a[i]*b[i];fft(a,-1);
	rep(i,0,n+m)printf("%d ",(int)(a[i].a/t+0.5));
	return 0;
} 

快速数论变换\(\rm NTT\),时间复杂度\(\rm O(N\log N)\)

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define pre(i,a,b) for(int i=a;i>=b;i--)
#define N 2100000
#define P 998244353
using namespace std;
int n,m,a[N],b[N],rev[N],t;
int Pow(int x,int y){
	if(y<0)return Pow(Pow(x,P-2),-y);
	int now=1;
	for(;y;y>>=1,x=1LL*x*x%P)if(y&1)now=1LL*now*x%P;
	return now;
}
void ntt(int *u,int op){
	rep(i,0,t-1)if(rev[i]>i)swap(u[i],u[rev[i]]);
	for(int l=1,k=2;k<=t;k<<=1,l<<=1){
		int cur=Pow(3,op*(P-1)/k);
		for(int i=0;i<t;i+=k){
			int now=1;
			rep(j,0,l-1){
				int x=u[i+j],y=1LL*now*u[i+j+l]%P;
				u[i+j]=(x+y)%P;u[i+j+l]=(x-y)%P;
				now=1LL*now*cur%P;
			}
		}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	rep(i,0,n)scanf("%d",&a[i]);
	rep(i,0,m)scanf("%d",&b[i]);
	t=1;while(t<=n+m)t<<=1;
	rep(i,1,t-1)rev[i]=(rev[i>>1]>>1)|((i&1)?(t>>1):0);
	ntt(a,1);ntt(b,1);rep(i,0,t-1)a[i]=1LL*a[i]*b[i]%P;
	ntt(a,-1);int cur=Pow(t,-1);
	rep(i,0,n+m)printf("%d ",(1LL*cur*a[i]%P+P)%P);
	return 0;
}

\(\rm FFT\) 会丢失精度,\(\rm NTT\) 必须对特殊质数取模。


P4781 【模板】拉格朗日插值

已知 \(N\)\(x_i\) 不相等的点,我们可以确定一个 \(N-1\) 次多项式。

拉格朗日插值可以在\(\rm O(N^2)\) 的时间内求出 \(\rm f(k)\)

我们构造函数

\[\large g_{i}=y_i\prod\limits_{j=1}^{n}[i\neq j]\dfrac{x-x_j}{x_i-x_j} \]

不难发现这个函数在 \(x_i\) 处取值为 \(y_i\),在 \(x_j\) 处取值为 \(0\) 。并且这是个 \(N-1\) 次多项式。

我们令\(f_i=\sum\limits_{i=1}^n g_i\),可以构造出过 \(N\) 个点的 \(N-1\) 次多项式。

#include<bits/stdc++.h>
#define rep(i,a,b) for(int i=a;i<=b;i++)
#define P 998244353
#define N 2005
using namespace std;
int Pow(int x,int y){
	int now=1;
	for(;y;y>>=1,x=1LL*x*x%P)if(y&1)now=1LL*now*x%P;
	return now;
}
int n,k,x[N],y[N],ans;
int main(){
	scanf("%d%d",&n,&k);
	rep(i,1,n)scanf("%d%d",&x[i],&y[i]);
	rep(i,1,n){
		int u=1,v=1;
		rep(j,1,n)if(i!=j)u=1LL*u*(k-x[j])%P,v=1LL*v*(x[i]-x[j])%P;
		ans=(ans+1LL*y[i]*u%P*Pow(v,P-2))%P;
	}
	printf("%d\n",(ans+P)%P);
	return 0;
}

P3338 [ZJOI2014]力

直接求式子\(N^2\),可以获得可观的 \(30\) 分。如果指令集优化一下甚至可以有 \(60\) 分。

但是如果不会多项式不能拿到高分。

下面是基础推式子:

\[E_i=\sum\limits_{j=1}^{i-1}\dfrac{q_j}{(i-j)^2}-\sum\limits_{j=i+1}^{n}\dfrac{q_j}{(i-j)^2} \]

不难发现 \(j+(i-j)=i\) 是一个定值,这就是两个卷积,直接\(\rm FFT\)即可。

Code


P3723 [AH2017/HNOI2017]礼物

旋转 \(k\) 下的差异值可以写出来。

\[Diff=\sum (x_i-y_{i+k})^2 \]

如果我们将\(y\)反转。

\[Diff=\sum (x_i-y_{k-i})^2 \]

拆开

\[Diff=\sum x^2_i+\sum y_i^2 +2\sum x_iy_{k-i} \]

注意这是一个循环卷积,但我们仍可以按照普通卷积来求。

差异值不会很大,直接\(\rm NTT\)即可。

Code

至于其它的,B卷应该不会考

posted @ 2021-12-16 17:25  7KByte  阅读(73)  评论(0编辑  收藏  举报