FFT & NTT & FWT 基础

FFT Part

公式

欧拉公式: \(e^{i\theta}=\cos{\theta}+i\sin{\theta}\)

特殊形式:\(e^{i\pi}=-1\)

Begin

一个多项式 \(F=x^2+x+1\)

简写作 \((1,1,1)\)

现在我们知道只需要三个点就可以唯一确定其图像 (玄学结论)

假如我们随意代数,则秦九条乱搞是 \(O(n^2)\)

DFT

于是我们设 \(w_n=e^{\frac{2i\pi}{n}}\)

代数 \(w_n^n=e^{2\pi i}=(e^{i\pi})^2=(-1)^2=1\)

我们代数的时候不带入 \(0,1,2,3,4,5...\)

而是带入 \(w_n^0,w_n^1,......\)

这就是 \(DFT\)

然而它仍然是暴力 \(N^2\) 复杂度

FFT

设一个多项式 \(A=x^7+2x^6+3x^5+4x^4+5x^3+6x^2+7x+8\)

则 拆开
\(A_1=x^6+3x^4+5x^2+7\)
\(A_2=2x^6+4x^4+6x^2+8\)

我们发现 \(A=xA_1+A_2\)

然后我们知道 \(FFT(A_1\ or\ A_2)\) 带入的其实是 \(w_4\)

但是他的 \(X\) 其实是 \(X^2\)
\(So\) \(w_4^i\) 其实对应实际上的 \(w_8^i\) 和 \(w_8^{i+4}\)

设 \(FFT(A,i)\) 是带入 \(w_{length(A)}^i\) 的结果

则 \(FFT(A,i)=FFT(A_2,i)+x FFT(A_1,i)=FFT(A_2,i)+w_{length(A)}^i FFT(A_1,i)\)
\(FFT(A,i+\frac{length(A)}2)=FFT(A_2,i)+x FFT(A_1,i)=FFT(A_2,i)+w_{length(A)}^{i+\frac{length(A)}2}FFT(A_1,i)=FFT(A_2,i)-w_{length(A)}^i FFT(A_1,i)\)

分治着去做就可以了

IFFT

直接将单位根变成 \(w'_n=e^{-\frac{2i\pi}{n}}\) 做 \(FFT\)

最后记得除以 \(length\)
具体过程别管了
你看不懂我也看不懂

实现

一般我们都写循环版本

至于二进制逆序之类,建议直接看板子

复数可手写可 \(STL\)

Code

P1919 A*B Problem

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
typedef double db;
const double pi=3.1415926535897932384626;
const int N=4000009;
typedef complex<db> com;
int rev[N];
void FFT(com *x,int len,db op){
	for(int i=0;i<len;i++)if(i<rev[i])swap(x[i],x[rev[i]]);
	for(int l=2;l<=len;l<<=1){
		com rt(cos(2*pi/(db)l),op*sin(2*pi/(db)l));
		for(int i=0;i<len;i+=l){
			com w(1,0);
			for(int r=i;r<=i+l/2-1;r++){
				com u=x[r],v=w*x[r+l/2];
				x[r]=u+v,x[r+l/2]=u-v;
				w*=rt;
			}
		}
	}
	if(op<0)for(int i=0;i<len;i++)x[i]/=(db)len;
}
com k[N],k2[N];
char a[N],b[N];
int la,lb,ans[N];
int main(){
	int len=1;
	scanf("%s%s",a,b),la=strlen(a),lb=strlen(b);
	for(int i=0;i<la;i++)k[i].real(a[la-i-1]-'0'),k[i].imag(0);
	for(int i=0;i<lb;i++)k2[i].real(b[lb-i-1]-'0'),k2[i].imag(0);
	while(len<la+lb-1)len<<=1;
	for(int i=0;i<len;i++){
		rev[i]=rev[i>>1]>>1;
		if(i&1)rev[i]+=len>>1;
	}
	FFT(k,len,1.0),FFT(k2,len,1.0);
	for(int i=0;i<len;i++)k[i]*=k2[i];
	FFT(k,len,-1.0);
	for(int i=0;i<la+lb-1;i++)ans[i]=(int)(k[i].real()+0.2);
	for(int i=0;i<la+lb-2;i++)ans[i+1]+=ans[i]/10,ans[i]%=10;
	for(int i=la+lb-2;i>=0;i--)printf("%d",ans[i]);
	return 0;
}

NTT Part

Reason For NTT

\(FFT\) 算法有一个问题:有些题目需要进行取模,而 \(FFT\) 的 \(double\) 显然做不到

所以我们能不能找一个可以取模的算法呢
\(NTT\) 就是解决这个问题的

但有一个很严重的问题

就是 \(NTT\) 并非任意模数

一般用得较多的是质数 \(998244353\)
\(So\ Let's\ Begin\)

Start

欧拉定理 \(a^{\phi(k)}\equiv 1 \mod k\)

当 \(k\) 为质数时得 \(a^{p-1}\equiv 1 \mod p\) 就是著名的费马小定理

接下来我们引入原根的概念
质数 \(i\) 的原根记作 \(g_i\)

(本文仅叙述方便,不保证叙述严格)

这个是啥意思呢

就是 \(g_i\) 的所有幂次能取遍所有模 \(i\) 的余数

并且他是有周期性的

显然 \(g_i^0=g_i^{i-1}=1\)

可知周期 \(C=i-1\)

这就能解释一个疑问:为什么必须是 \(998244353\) 等几个有限的数

首先,这个模数必须是一个质数,要不他不满足费马小定理

然后,\(998244353=119×2^{23}+1\)

所以这个模数的好处就是他的周期是 \(q*2^k\)

NTT

还记得刚才讲的 \(FFT\) 吗?

我们是怎么做的?

我们知道他是表示成 \(A= xA_1+A_2\) 的形式

因为我们是分治的方法

所以我们代入得其实就是 \(2^k\) 个 \(Value\)

而且要保证他有周期性

平方之后对应上一层的 \(Value\)

我们上一次是用复数来实现的

但是其实原根也可以

我们想一下这个单位元是多少

首先我们定义 \(w_i\) 为一个周期为 \(i\) 的单位元

所以原来的朴素原根 \(g_i\) 周期为 \(q*2^k\) 即 \(P-1\)

如果想要缩短其周期那么必须进行乘方

所以我们容易得知 \(w_i=g^\frac{P-1}{i}\)

而显然 \(i\) 为 \(2^k\) 如果他的周期不能整除那肯定不行

然后就没有了

当然一般你的序列长度是会小于 \(2^{23}\) 即 \(8e6\) 的

所以这个模数基本上是够的

A Small Problem

现在只有一个问题:怎么求原根?
我才不会告诉你我也不知道
\(998244353,1004535809\) 的原根都是 \(3\)

实现细节

我们的单位根和 \(FFT\) 一模一样

所以 \(INTT\) 、 位逆序等等都都是一模一样的

所以 \(INTT\) 也需要乘 \(1/n\) ,单位元也需要变成逆元

Code [P1919]

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=4000009;
const ll Mod=998244353,Root=3;
int len=1,rev[N];
ll Pow(ll d,ll x){
	ll ans=1,tmp=d;
	while(x){
		if(x&1)ans=(ans*tmp)%Mod;
		tmp=(tmp*tmp)%Mod;
		x>>=1;
	}
	return ans;
}
ll Inv(ll u){return (Pow(u,Mod-2)+Mod)%Mod;}
void NTT(ll *x,bool I){
	for(int i=0;i<len;i++)if(i<rev[i])swap(x[i],x[rev[i]]);
	for(int l=2;l<=len;l<<=1){
		ll rt=Pow(Root,(Mod-1)/l);
		if(I)rt=Inv(rt);
		for(int i=0;i<len;i+=l){
			ll w=1;
			for(int j=i;j<=i+l/2-1;j++){
				ll u=x[j],v=w*x[j+l/2]%Mod;
				x[j]=(u+v)%Mod,x[j+l/2]=(u-v+Mod)%Mod;
				w=(w*rt)%Mod;
			}
		}
	}
	ll Inv_n=Inv(len);
	if(I)for(int i=0;i<len;i++)x[i]=(x[i]*Inv_n)%Mod;
}
ll a[N],b[N];
int main(){
	int la=-1,lb=-1;
	char k=getchar();
	while(!isdigit(k))k=getchar();
	while(isdigit(k))a[++la]=k-'0',k=getchar();
	while(!isdigit(k))k=getchar();
	while(isdigit(k))b[++lb]=k-'0',k=getchar();
	for(int i=0;i<la-i;i++)swap(a[i],a[la-i]);
	for(int i=0;i<lb-i;i++)swap(b[i],b[lb-i]);
	while(len<la+lb+1)len<<=1;   
	for(int i=0;i<len;i++){
		rev[i]=rev[i>>1]>>1;
		if(i&1)rev[i]|=len>>1;
	}
	NTT(a,0),NTT(b,0);
	for(int i=0;i<len;i++)a[i]=(a[i]*b[i])%Mod;
	NTT(a,1);
	for(int i=0;i<la+lb;i++)a[i+1]+=a[i]/10,a[i]%=10;
	int i=la+lb;
	while(i>0&&a[i]==0)--i;
	for(;i>=0;i--)printf("%lld",a[i]);
	return 0;
}

FWT Part

前言

本部分推荐配合 OI-WiKi 使用

并大量参考了其中内容

Reason For FWT

\(FFT\) 和 \(NTT\) 求多项式乘法已经很不错了

但我们写一下多项式乘法的一种形式

\[C[i]=\sum_{j+k=i}{A[j]*B[k]} \]

其中 \(C\) 是答案数组
\(A,B,C[i]\) 表示的是这个多项式次数为 \(i\) 的项的系数

而有的时候毒瘤题目要求的是下面的式子

\[C[i]=\sum_{j\oplus k=i}{A[j]*B[k]} \]

其中的 \(\oplus\) 可能是与,或,异或。

这个东西 \(FWT\) 可以搞

Start

我们的整体思路仍然是转换 \(A\) \(B\) 数组的形式

让它变成一种奇怪的表示方法

然后可以 \(O(n)\) 计算

然后再转换回去

记住这个思路,后面会用

同时, \(FWT\) 和我们前两个算法的差异还是很大的,勿硬套

\(FWT\) 算法有一个答案 \(C\) 数组和三个 \(FWT\) 数组(ABC 各开一个)(可以直接用各自的数组,不需要真的开出来)

下文写作 \(FAT\) \(FBT\) \(FCT\)

如对三个数组都满足则写作 \(FWT\)

如果指的是 \(ABC\) 则写作 \(W\)
\(OK,Let's\ Go!\)

OR

现在我们要干什么?

求 \(C\) 数组。

\(C\) 数组满足什么?
\(C_i=\sum_{i=j|k}{A_jB_k}\) 。

那么我们的 \(FWT\) 是干什么的?

沃尔什大佬定义了 \(FWT\) 的定义和计算方式

即 \(FAT_i*FBT_i=FCT_i\)

而想要保证 \(FWT_i=\sum_{j|i=i}{W_j}\)

注意 \(FWT\) 数组和 \(W\) 数组不一样!

我们发现当 \(FAT\) \(FBT\) 都满足性质的时候 \(FCT\) 同样满足性质

抄一个 OI-Wiki 上的公式

\[\begin{aligned} FAT_i\cdot FBT_i&=\left(\sum_{i|j=i} A_j\right)\left(\sum_{i|k=i} B_k\right) \\ &=\sum_{i|j=i}\sum_{i|k=i}A_jB_k \\ &=\sum_{i|(j|k)=i}A_jB_k \\ &= FCT_i \end{aligned} \]

最后两步好好看看

对照一下 \(C_i\) 和 \(FWT_i\) 的公式
\(You\ Will\ Get\ It.\)

那么我们接下来看怎么求一段区间的 \(FWT\)

暴力不用想, \(O(n^2)\)

显然如果只有一个数的区间 \(FWT\) 直接 \(CV\) 原数据即可

显然如果只有一个数的区间 \(FWT\) 直接 \(CV\) 原数据即可,不多赘述
根据我们某某T的学习经验,我们一定是要分治实现 \(n\log n\) 的
\(FWT\) 的根本思想是:

既然你的操作都是基于二进制的

那我就用二分把每个下标的二进制位拆出来!

我们令 \(Q\) 表示当前**\(FWT\)** 区间的前一半,\(H\) 表示区间的后一半。(Qian Hou qwq)

显然,如果你从 \(0\) 开始算下标的话
\(Q\) 的下标二进制最高位都是 \(0\)
\(H\) 的下标二进制最高位都是 \(1\)

注:知道各位 dalao 在想什么,这里其实说法不严谨,比如说我们现在的一段区间 \([2,3]\) ,二进制表示是 \(10\) 和 \(11\),他的前面是可能有一个相同的前缀的。但联系当时我们 \(FFT\) 的算法,这里我们不把区间当成原序列的一部分,而是把他们当成完整的我们想求的区间 而我们已经知道了他们左右区间"单独" \(FWT\) 的值,然后我们只需要将他们合并即可,此处亦可将其定义成"去掉前缀后的下标二进制最高位"。

我们发现,合并后的 \(FWT\) 数组,\(Q\) 部分不会受到后面影响(因为 \(H\) 部分所有的下标上都会有一个额外的 \(1\))

但 \(H\) 数组却需要加上 \(Q\) 数组

因为这是 \(OR\) !

你问咋加? 语法入门循环结构,直接加!

至于求逆,由于他的数组是 \(H\) \(H+Q\)

所以直接 \(Q-=H\) 即可

放个详细注释版的 \(Oi-Wiki\) 代码

cpp 复制代码
void Or(ll *a, ll type) {  // 迭代实现,常数更小
  for (ll x = 2; x <= n; x <<= 1) {//区间长度
    ll k = x >> 1;
    for (ll i = 0; i < n; i += x) {//枚举区间左端点
      for (ll j = 0; j < k; j++) {//枚举 H 中的每个点
        (a[i + j + k] += a[i + j] * type) %= P;
        //type 为 1 时是 FWT,-1 是 IFWT
      }
    }
  }
}

是不是很简单?好了我们继续!

AND

Wiki 此部分未给出证明

但我们还是要简单搞一下的

根据常识,沃尔什大佬不会轻易改基础定义

所以我们把基础公式搞过来

\[FAT_i*FBT_i=FCT_i \]

\[FWT_i=\sum_{j\&i=i}{W_j} \]

\[C_i=\sum_{i=j\&k}{A_jB_k} \]

再搞一个证明公式

\[\begin{aligned} FAT_i\cdot FBT_i&=\left(\sum_{i\&j=i} A_j\right)\left(\sum_{i\&k=i} B_k\right) \\ &=\sum_{i\&j=i}\sum_{i\&k=i}A_jB_k \\ &=\sum_{i\&(j\&k)=i}A_jB_k \\ &= FCT_i \end{aligned} \]

我们发现 and 运算也是满足 \(a\&b=a,a\&c=a \to a\&(b\&c)=a\) 的

所以它可以

问题不大

接下来我们仍借用原来的定义进行分析

显然,我们发现 \(H\) 数组是不受 \(Q\) 的数组影响的

但是我们发现 \(Q\) 数组需加上 \(H\) 数组

所以
\(FWT:[Q+H]+[H]\)
\(IFWT:[Q-H]+[H]\)

\(OI-WiKi\ Std\)

cpp 复制代码
void And(ll *a, ll type) {
  for (ll x = 2; x <= n; x <<= 1) {
    ll k = x >> 1;
    for (ll i = 0; i < n; i += x) {
      for (ll j = 0; j < k; j++) {
        (a[i + j] += a[i + j + k] * type) %= P;
      }
    }
  }
}

XOR

这个才是强度所在

这个的基础公式和前面小有区别

(以下作者并不明白为什么,但他是一种正确的构造)

首先设 \(a \# b\) 表示表示 \(x\& y\) 二进制表示中 \(1\) 数量的奇偶性

具体地讲,奇数时值为一,偶数时值为零

基础公式

\[FAT_i*FBT_i=FCT_i \]

\[FAT_i=\sum_{i\# j=0}A_j-\sum_{i\# j=1}A_j \]

\[C_i=\sum_{i=j\oplus k}{A_jB_k} \]

接下来我们证一下构造的正确性

\[\begin{aligned} FAT_iFBT_i&=\left(\sum_{i\# j=0}A_j-\sum_{i\# j=1}A_j\right)\left(\sum_{i\# k=0}B_k-\sum_{i\# k=1}B_k\right) \\ &=\left(\sum_{i\# j=0}A_j\sum_{i\# k=0}B_k+\sum_{i\# j=1}A_j\sum_{i\# k=1}B_k\right)-\left(\sum_{i\# j=0}A_j\sum_{i\# k=1}B_k+\sum_{i\# j=1}A_j\sum_{i\# k=0}B_k\right) \\ &=\sum_{(j\oplus k)\# i=0}A_jB_k-\sum_{(j\oplus k)\# i=1}A_jB_k \\ &=FCT_i \end{aligned} \]

如你所见,这个公式不太像是给人看的,所以我们解释一下

Line 1: 根据基础公式带入

Line 2: 乘法分配律

Line 3: 这一步的过程比较恶心,我们其实本质上就是要证明 \((i\#j)\oplus(i\#k)=i\#(j\oplus k)\)

Line 4: 定义带入

然后我们看一下 \((i\#j)\oplus(i\#k)=i\#(j\oplus k)\)

问题是:他对吗?

我也不知道好像吧。。。

我们例证一下
\(i=1101\ j=1001\ k=10\)

左边\(=0\oplus0=0\)

右边\(=1101\#0=0\)

好了就这样吧

接下来我们看一下这个 \(FWT\) 咋求

首先分治是一定的

然后我们考虑 \(Q\) 和 \(H\) 数组搞完变成什么了

先看我们的 \(Q\) 数组

首先他自己原来的 \(Value\) 可以用

其次我们考虑另一边的 \(H\) 数组

我们每一次都是不考虑他们的前缀的

所以呢?

所以右边和他位置相对应的 \(H\) 数组他在做这个 \(FWT\) 的时候他的方式和左边是一样的

而当前左边的首位为零

与完并没有什么影响

所以左边应为 \([Q+H]\)

后半部分呢?

左边因为最高位是零也可以直接加到右边

右边的 \(H\) 由于最高位给补了一个一而导致所有的东西全反了
\(SO\)
\([Q+H]+[Q-H]\)

反向操作也很简单啦
\([(Q+H)/2]+[(Q-H)/2]\)
这不就被我们口胡出来了吗!
\(Code\ From\ Wiki\)

cpp 复制代码
void Xor(ll *a, ll type) {
  for (ll x = 2; x <= n; x <<= 1) {
    ll k = x >> 1;
    for (ll i = 0; i < n; i += x) {
      for (ll j = 0; j < k; j++) {
        (a[i + j] += a[i + j + k]) %= P;
        (a[i + j + k] = a[i + j] - a[i + j + k] * 2) %= P;
        (a[i + j] *= type) %= P;
        (a[i + j + k] *= type) %= P;
      }
    }
  }
}

现在你可以水一个紫题 P4717

Tips

  1. 建议按照 \(WiKi\) 的模板的马蜂,因为他如果把反操作单独拆开可能函数的数量太多
  2. \(FWT\) 不需要二进制逆序!!!!!
  3. 这东西只有加减,所以可以取模,爱模多少模多少

Code

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1000009;
const ll Mod=998244353,Inv=499122177;
int n;
ll a[N],b[N],ta[N],tb[N],ans[N];
void MUL(ll *a,const ll *b){
	for(int i=0;i<n;i++)a[i]=(a[i]*b[i])%Mod;
}
void SHOW(const ll *x){
	for(int i=0;i<n;i++)printf("%lld ",x[i]);
	puts("");
}
void OR(const ll *_x,ll *x,ll type){
	int tmp;
	for(int i=0;i<n;i++)x[i]=_x[i];
	for(int l=2;l<=n;l<<=1){
		tmp=l>>1;
		for(int i=0;i<n;i+=l)for(int j=i;j<i+tmp;j++)
			x[j+tmp]=((x[j+tmp]+type*x[j])%Mod+Mod)%Mod;
	}
}
void AND(const ll *_x,ll *x,ll type){
	int tmp;
	for(int i=0;i<n;i++)x[i]=_x[i];
	for(int l=2;l<=n;l<<=1){
		tmp=l>>1;
		for(int i=0;i<n;i+=l)for(int j=i;j<i+tmp;j++)
			x[j]=((x[j]+type*x[j+tmp])%Mod+Mod)%Mod;
	}
}
void XOR(const ll *_x,ll *x,ll type){
	int tmp;
	ll Q,H;
	for(int i=0;i<n;i++)x[i]=_x[i];
	for(int l=2;l<=n;l<<=1){
		tmp=l>>1;
		for(int i=0;i<n;i+=l)for(int j=i;j<i+tmp;j++){
			Q=x[j],H=x[j+tmp];
			x[j]=(Q+H)%Mod*type%Mod;
			x[j+tmp]=((Q-H)%Mod*type%Mod+Mod)%Mod;
		}
	}
}
int main(){
	int tmp;
	scanf("%d",&tmp),n=1<<tmp;
	for(int i=0;i<n;i++)scanf("%lld",&a[i]);
	for(int i=0;i<n;i++)scanf("%lld",&b[i]);
	OR(a,ta,1),OR(b,tb,1),MUL(ta,tb),OR(ta,ans,-1),SHOW(ans);
	AND(a,ta,1),AND(b,tb,1),MUL(ta,tb),AND(ta,ans,-1),SHOW(ans);
	XOR(a,ta,1),XOR(b,tb,1),MUL(ta,tb),XOR(ta,ans,Inv),SHOW(ans);
	return 0;
}

例题

P3723 礼物

求 \(min \sum_{i=1}^n{(x_i-y_i+c)^2}\ \ (1<=n<=50000,-500<=c<=500)\)

Solution

直接展开

原式 \(=\sum{x_i^2+y_i^2+c^2+2x_ic-2y_ic-2x_iy_i}\)
\(=\sum{x_i^2}+\sum{y_i^2}+nc^2+2c\sum{x_i}-2c\sum{y_i}-2\sum{x_iy_i}\)

只需要最大化最后一项即可
\(c\) 怎么办呢?枚举!
\(\sum{x_iy_i}\) 看似无法处理
实则也确实无法处理

有个小 \(trick\) ,把 \(x\) 数组倒置过来

然后就变成了 \(\sum{x_{n-i+1}y_i}\)

看似还是白扯

但我们发现这样的项有一个性质

其下标相加为 \(n+1\) !

所以我们把每一个下标都当作次数
\(Fast\ Fast\ TLE\) 搞一搞就可以了

但我们想到一颗问题

就是旋转则么办?

我们设第一个手环的偏移量为 \(\theta\) 且 \(0<=\theta<=n-1\)

然后就变成了 \(\sum{x_{n-i+1+\theta}y_i}\)

求和为 \(n+1<=n+\theta+1<=2n\)

只需要找最大的系数即可

Code
cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
typedef double db;
typedef long long ll;
typedef complex<db> com;
const int N=300009;
const db pi=3.1415926535897932385626;
int rev[N],len;
ll asum,bsum,asq,bsq,ans=LONG_LONG_MAX;
void FFT(com *x,db op){
	for(int i=0;i<len;i++)if(i<rev[i])swap(x[i],x[rev[i]]);
	for(int l=2;l<=len;l<<=1){
		com rt(cos(2.0*pi/(db)l),op*sin(2.0*pi/(db)l));
		for(int i=0;i<len;i+=l){
			com w(1,0);
			for(int r=i;r<i+l/2;r++){
				com u=x[r],v=w*x[r+l/2];
				x[r]=u+v,x[r+l/2]=u-v;
				w*=rt;
			}
		}
	}
	if(op<0)for(int i=0;i<len;i++)x[i]/=(db)len;
}
com a[N],b[N];

int main(){
	int n,m,tmp;
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;i++)scanf("%d",&tmp),a[n-1-i].real(tmp),a[n+n-1-i].real(tmp),asum+=tmp,asq+=tmp*tmp;
	for(int j=0;j<n;j++)scanf("%d",&tmp),b[j].real(tmp),bsum+=tmp,bsq+=tmp*tmp;
	len=1;
	while(len<n+n+n-1)len<<=1;
	for(int i=0;i<len;i++){
		rev[i]=rev[i>>1]>>1;
		if(i&1)rev[i]|=len>>1;
	}
	FFT(a,1.0),FFT(b,1.0);
	for(int i=0;i<len;i++)a[i]*=b[i];
	FFT(a,-1.0);
	for(int i=n+1;i<=n+n;i++){
		for(int c=-m;c<=m;c++){
			//if(fabs(a[i].imag())>0.5)puts("Fuck!"),exit(0);
			ans=min(ans,asq+bsq+n*c*c+2ll*asum*c-2ll*bsum*c-2ll*(ll)(a[i].real()+0.2));
		}
	}
	printf("%lld",ans);
	return 0;
}