[數學/多項式] FFT Practice

T1 多項式乘法(FFT)

板子

#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define int long long
inline int in{
	int i=0,f=1;char ch=0;
	while(ch!='-'&&!isdigit(ch)) ch=getchar();
	if(ch=='-') ch=getchar(),f=-1;
	while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int NNN=1e7+5;
const double pi=acos(-1);
int n,m,rot[NNN];
struct Complex{
	double x,y;
	Complex(double _x=0,double _y=0){x=_x,y=_y;}
	friend inline Complex operator + (const Complex u,const Complex v){return Complex(u.x+v.x,u.y+v.y);}
	friend inline Complex operator - (const Complex u,const Complex v){return Complex(u.x-v.x,u.y-v.y);}
	friend inline Complex operator * (const Complex u,const Complex v){return Complex(u.x*v.x-u.y*v.y,u.x*v.y+v.x*u.y);}
}A[NNN],B[NNN],C[NNN];

inline void FFT(Complex *f,int lim,int sgn){
	for(int i=0;i<lim;++i)
		if(i<rot[i]) swap(f[i],f[rot[i]]);
	for(int siz=2;siz<=lim;siz<<=1){
		int len=siz>>1;
		Complex wn(cos(2*pi/siz),sgn*sin(2*pi/siz));
		for(int l=0;l<lim;l+=siz){
			Complex w(1,0);
			for(int i=l;i<l+len;++i){
				Complex tmp=w*f[i+len];
				f[i+len]=f[i]-tmp;
				f[i]=f[i]+tmp;
				w=w*wn;
			}
		}
	}
}

signed main(){
	n=in,m=in;
	for(int i=0;i<=n;++i) A[i].x=in;
	for(int i=0;i<=m;++i) B[i].x=in;
	int lim=1;
	while(lim<=n+m) lim<<=1;
	for(int i=0;i<lim;++i) rot[i]=((rot[i>>1]>>1)|((i&1)?lim>>1:0));
	FFT(A,lim,1);
	FFT(B,lim,1);
	for(int i=0;i<=lim;++i) C[i]=A[i]*B[i];
	FFT(C,lim,-1);
	for(int i=0;i<=m+n;++i) printf("%lld ",(int)(C[i].x/lim+0.49));
	return 0;
}

T2 A*B Problem升級版(FFT快速傅里葉)

注意最下面輸出答案的操作

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

const int NNN=5e6+10;
const double pi=acos(-1);
int n,m,rev[NNN],ans[NNN];
char N[NNN],M[NNN];

struct Complex{
	double x,y;
	Complex(double xx=0,double yy=0){x=xx,y=yy;}
	friend inline Complex operator + (const Complex &u,const Complex &v)
		{return Complex(u.x+v.x,u.y+v.y);}
	friend inline Complex operator - (const Complex &u,const Complex &v)
		{return Complex(u.x-v.x,u.y-v.y);}
	friend inline Complex operator * (const Complex &u,const Complex &v)
		{return Complex(u.x*v.x-u.y*v.y,u.x*v.y+u.y*v.x);}
}A[NNN],B[NNN],C[NNN];

inline void FFT(Complex *f,int lim,int sgn){
	for(int i=0;i<lim;++i)
		if(i<rev[i]) swap(f[i],f[rev[i]]);
	for(int siz=2;siz<=lim;siz<<=1){
		int len=siz>>1;
		Complex wn(cos(2*pi/siz),sgn*sin(2*pi/siz));
		for(int l=0;l<lim;l+=siz){
			Complex w(1,0);
			for(int i=l;i<l+len;++i){
				Complex tmp=f[i+len]*w;
				f[i+len]=f[i]-tmp;
				f[i]=f[i]+tmp;
				w=w*wn;
			}
		}
	}
}

int main(){
	scanf("%s%s",N+1,M+1);
	n=strlen(N+1);
	m=strlen(M+1);
	for(int i=1;i<=n;++i) A[n-i].x=(double)(N[i]-'0');--n;
	for(int i=1;i<=m;++i) B[m-i].x=(double)(M[i]-'0');--m;
	
//	for(int i=0;i<=n;++i) printf("%.2lf ",A[i].x);puts("");
//	for(int i=0;i<=m;++i) printf("%.2lf ",B[i].x);puts("");
	
	int lim=1;
	while(lim<=n+m) lim<<=1;
	for(int i=0;i<=lim;++i) rev[i]=(rev[i>>1]>>1)|((i&1)?lim>>1:0);
	FFT(A,lim,1);
	FFT(B,lim,1);
	for(int i=0;i<=lim;++i) C[i]=A[i]*B[i];
	FFT(C,lim,-1);
	for(int i=0;i<=lim;++i){
		ans[i]+=(int)(C[i].x/lim+0.49);
		if(ans[i]>=10){
			ans[i+1]+=ans[i]/10;
			ans[i]%=10;
			lim+=(i==lim);
		}
	}
	while(!ans[lim]&&lim>=1) --lim;
	while(lim>=0) printf("%d",ans[lim]),--lim;
	return 0;
}

T3 Force

E[j]=i=1j1qi(ij)2i=j+1nqi(ij)2=i=1jqi(ij)2i=jnqi(ij)2 \begin{aligned} E[j]&=\sum_{i=1}^{j-1}\frac{q_i}{(i-j)^2}-\sum_{i=j+1}^{n}\frac{q_i}{(i-j)^2}\\ &=\sum_{i=1}^{j}\frac{q_i}{(i-j)^2}-\sum_{i=j}^{n}\frac{q_i}{(i-j)^2}\\ \end{aligned}

定義1(ij)2\frac{1}{(i-j)^2}i=ji=j時值爲00

定義f[i]=qi, g[i]=1i2f[i]=q_i,\ g[i]=\frac{1}{i^2}f[0]=0, g[0]=0f[0]=0,\ g[0]=0
E[j]=i=1jf[i]×g[ji]i=jnf[i]×g[ji]=i=0jf[i]×g[ji]i=jnf[i]×g[ji]=ΔC[i]D[i] \begin{aligned} E[j]&=\sum_{i=1}^{j}f[i]\times g[j-i]-\sum_{i=j}^{n}f[i]\times g[j-i]\\ &=\sum_{i=0}^{j}f[i]\times g[j-i]-\sum_{i=j}^{n}f[i]\times g[j-i]\\ &\mathop{=}^\Delta C[i]-D[i] \end{aligned}
其中CC爲卷積結果,可以FFT

對於DD
D[i]=i=jnf[i]×g[ji]=i=0njf[i+j]×g[i]g[i]=g[i] \begin{aligned} D[i]&=\sum_{i=j}^{n}f[i]\times g[j-i]\\ &=\sum_{i=0}^{n-j}f[i+j]\times g[i]&&\cdots g[i]=g[-i] \end{aligned}
f1[i]=f[ni]f_1[i]=f[n-i],常見翻轉操作
D[i]=i=0njf1[(nj)i]×g[i] \begin{aligned} D[i]&=\sum_{i=0}^{n-j}f_1[(n-j)-i]\times g[i] \end{aligned}
DD也變成了卷積

A=f, B=g, C=f1A=f,\ B=g,\ C=f_1,則C=AB, D=BCC=A*B,\ D=B*C
ans[i]=C[i]D[ni]ans[i]=C[i]-D[n-i]

對於BB1.0/(i*i)會被卡常,優化1.0/i/i

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

const int NNN=5e5+5;
const double pi=acos(-1);
int n,rev[NNN];
double ans[NNN];

struct Complex{
	double x,y;
	Complex(double xx=0,double yy=0){x=xx,y=yy;}
	friend inline Complex operator + (const Complex &u,const Complex &v){return Complex(u.x+v.x,u.y+v.y);}
	friend inline Complex operator - (const Complex &u,const Complex &v){return Complex(u.x-v.x,u.y-v.y);}
	friend inline Complex operator * (const Complex &u,const Complex &v){return Complex(u.x*v.x-u.y*v.y,u.x*v.y+u.y*v.x);}
}A[NNN],B[NNN],C[NNN],D[NNN],G[NNN];

inline void FFT(Complex *f,int lim,int sgn){
	for(int i=0;i<=lim;++i)
		if(i<rev[i]) swap(f[i],f[rev[i]]);
	for(int siz=2;siz<=lim;siz<<=1){
		int len=siz>>1;
		Complex wn(cos(2*pi/siz),sgn*sin(2*pi/siz));
		for(int l=0;l<lim;l+=siz){
			Complex w(1,0);
			for(int i=l;i<l+len;++i){
				Complex tmp=w*f[i+len];
				f[i+len]=f[i]-tmp;
				f[i]=f[i]+tmp;
				w=w*wn;
			}
		}
	}
}

int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i){
		scanf("%lf",&A[i].x);
		C[n-i].x=A[i].x;
		B[i].x=(double)1.0/i/i;
	}
	
//	for(int i=1;i<=n;++i) printf("%.3lf,%.3lf,%.3lf\n",A[i].x,B[i].x,C[i].x);
	
	int lim=1;
	while(lim<=n+n) lim<<=1;
	for(int i=0;i<=lim;++i) rev[i]=(rev[i>>1]>>1)|((i&1)?lim>>1:0);
	FFT(A,lim,1);
	FFT(B,lim,1);
	FFT(C,lim,1);
	for(int i=0;i<=lim;++i) D[i]=B[i]*C[i],G[i]=A[i]*B[i];
	FFT(D,lim,-1);
	FFT(G,lim,-1);
	for(int i=1;i<=n;++i) printf("%.3lf\n",G[i].x/lim-D[n-i].x/lim);
	return 0;
}

T4 Gift

先說,這題的 m 告訴的是你最後增加亮度相對值 c 的上下界 [-m,m]

那麼我們一起推柿子
設增加亮度的相對值爲xx,旋轉後亮度數組爲{a},{b}\{a\},\{b\}
minimize(i=1n(aibi+x)2)i=1n(aibi+x)2=i=1n(ai2+bi2+x22aibi+2(aibi)x)=i=1nai2+i=1nbi2+nx2+2(i=1naii=1nbi)x2i=1naibi minimize(\sum_{i=1}^n(a_i-b_i+x)^2)\\ \begin{aligned} &\quad\sum_{i=1}^n(a_i-b_i+x)^2\\ &=\sum_{i=1}^n(a_i^2+b_i^2+x^2-2a_ib_i+2(a_i-b_i)x)\\ &=\sum_{i=1}^na_i^2+\sum_{i=1}^nb_i^2+nx^2+2(\sum_{i=1}^na_i-\sum_{i=1}^nb_i)x-2\sum_{i=1}^na_ib_i\\ \end{aligned}
發現只有i=1naibi\sum_{i=1}^na_ib_i不確定(nx2+2(i=1naii=1nbi)xnx^2+2(\sum_{i=1}^na_i-\sum_{i=1}^nb_i)x是關於xx的二次函數,可以回小學學一學最值的求法),於是最小化i=1naibi\sum_{i=1}^na_ib_i

常用的翻轉操作
發現所求式中兩量下標/自變量同增,步長相等時,考慮翻轉一個,得到卷積
i=1naibi\sum_{i=1}^na_ib_i,將aa翻轉,得到i=1nani+1bi\sum_{i=1}^na'_{n-i+1}b_i(卷積要保證下標和一定,且對稱)

注意邊界

破環爲鏈:倍長數組

#include<bits/stdc++.h>
using namespace std;
#define in Read()
#define int long long
inline int in{
	int i=0,f=1;char ch=0;
	while(ch!='-'&&!isdigit(ch)) ch=getchar();
	if(ch=='-') ch=getchar(),f=-1;
	while(isdigit(ch)) i=(i<<1)+(i<<3)+ch-48,ch=getchar();
	return i*f;
}

const int NNN=5e5+5;
const int INF=1e15+5;
const double pi=acos(-1);
int n,m,ans=INF;
int a[NNN],b[NNN];
int sqr_a,sqr_b;
int sum_a,sum_b;
int rev[NNN];

struct Complex{
	double x,y;
	Complex(double xx=0,double yy=0){x=xx,y=yy;}
	friend inline Complex operator + (const Complex &u,const Complex &v)
		{return Complex(u.x+v.x,u.y+v.y);}
	friend inline Complex operator - (const Complex &u,const Complex &v)
		{return Complex(u.x-v.x,u.y-v.y);}
	friend inline Complex operator * (const Complex &u,const Complex &v)
		{return Complex(u.x*v.x-u.y*v.y,u.x*v.y+u.y*v.x);}
}A[NNN],B[NNN];

inline void FFT(Complex *f,int lim,int sgn){
	for(int i=0;i<=lim;++i)
		if(i<rev[i]) swap(f[i],f[rev[i]]);
	for(int siz=2;siz<=lim;siz<<=1){
		int len=siz>>1;
		Complex wn(cos(2*pi/siz),sgn*sin(2*pi/siz));
		for(int l=0;l<lim;l+=siz){
			Complex w(1,0);
			for(int i=l;i<l+len;++i){
				Complex tmp=f[i+len]*w;
				f[i+len]=f[i]-tmp;
				f[i]=f[i]+tmp;
				w=w*wn;
			}
		}
	}
}

signed main(){
	n=in,m=in;
	for(int i=1;i<=n;++i){
		a[i]=in;
		A[i].x=A[i+n].x=(double)a[i];
		sqr_a+=a[i]*a[i];
		sum_a+=a[i];
	}
	for(int i=1;i<=n;++i){
		b[i]=in;
		B[n-i+1].x=(double)b[i];
		sqr_b+=b[i]*b[i];
		sum_b+=b[i];
	}
	
	int lim=1;
	while(lim<=n*3) lim<<=1;
	for(int i=0;i<=lim;++i) rev[i]=(rev[i>>1]>>1)|((i&1)?(lim>>1):0);
	
	FFT(A,lim,1);
	FFT(B,lim,1);
	for(int i=0;i<=lim;++i) A[i]=A[i]*B[i];
	FFT(A,lim,-1);
	for(int i=0;i<=lim;++i) A[i].x=(int)(A[i].x/lim+0.49);
	
	for(int i=1;i<=n;++i)
		for(int x=-m;x<=m;++x){
			int tmp=n*x*x+sqr_a+sqr_b;
			tmp+=2*(sum_a-sum_b)*x;
			ans=min(ans,tmp-2*(int)A[i+n].x);
		}
	
	printf("%lld\n",ans);
	
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章