loj2020 「HNOI2017」礼物

所有的下标从 (0) 开始。

考虑枚举 (C) (第一个加上负的等于第二个加上其绝对值)和第二个手链的偏移量 (p)。答案就是

[sum_{i=0}^{n-1}(x_i+C-y_{(i+p) mod n})^2 ]

复制一遍 (y) 数组就能去掉取模了,再展开就是

[sum_{i=0}^{n-1}((x_i+C)^2-2(x_i+C)y_{i+p}+y_{i+p}^2) ]

再展开就是

[sum_{i=0}^{n-1}(x_i+C)^2-2sum_{i=0}^{n-1}x_iy_{i+p}-2Csum_{i=0}^{n-1}y_i+sum_{i=0}^{n-1}y_i^2 ]

发现除了第二项别的都可以预处理后在枚举 (C)(O(1)) 得到,问题在于怎样快速求第二项。将 (x) 数组翻转就成了

[sum_{i=0}^{n-1}x_{n-i-1}y_{i+p} ]

显然 (p) 所对应的数就是 fft 后的第 (n+p-1) 项。fft 一次后枚举 (C,p) 即可。当然你也可以三分 (C)

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long ll;
int n, m, xx[50005], yy[50005], lim=1, tmpcnt, rev[300005];
ll ans=0x3f3f3f3f3f3f3f3f, sumxi, sumyi, sumxifang, sumyifang;
const double PI=acos(-1.0);
struct Complex{
	double x, y;
	Complex(double xx=0.0, double yy=0.0){
		x = xx;
		y = yy;
	}
	Complex operator+(const Complex &u)const{
		return Complex(x+u.x, y+u.y);
	}
	Complex operator-(const Complex &u)const{
		return Complex(x-u.x, y-u.y);
	}
	Complex operator*(const Complex &u)const{
		return Complex(x*u.x-y*u.y, x*u.y+y*u.x);
	}
}A[300005], B[300005];
void fft(Complex a[], int opt){
	for(int i=0; i<lim; i++)
		if(i<rev[i])
			swap(a[i], a[rev[i]]);
	for(int i=2; i<=lim; i<<=1){
		Complex wn=Complex(cos(PI*2/i), opt*sin(PI*2/i));
		int tmp=i>>1;
		for(int j=0; j<lim; j+=i){
			Complex w=Complex(1.0, 0.0);
			for(int k=0; k<tmp; k++){
				Complex tmp1=a[j+k], tmp2=w*a[j+k+tmp];
				a[j+k] = tmp1 + tmp2;
				a[j+k+tmp] = tmp1 - tmp2;
				w = w * wn;
			}
		}
	}
	if(opt<0)
		for(int i=0; i<lim; i++)
			a[i].x /= lim;
}
int main(){
	cin>>n>>m;
	for(int i=0; i<n; i++){
		scanf("%d", &xx[i]);
		sumxi += xx[i];
		sumxifang += xx[i] * xx[i];
		A[n-i-1].x = xx[i];
	}
	for(int i=0; i<n; i++){
		scanf("%d", &yy[i]);
		sumyi += yy[i];
		sumyifang += yy[i] * yy[i];
		B[i+n].x = B[i].x = yy[i];
	}
	while(lim<=3*n)	lim <<= 1, tmpcnt++;
	for(int i=0; i<lim; i++)
		rev[i] = (rev[i>>1]>>1) | ((i&1)<<(tmpcnt-1));
	fft(A, 1);
	fft(B, 1);
	for(int i=0; i<lim; i++)
		A[i] = A[i] * B[i];
	fft(A, -1);
	for(int C=-m; C<=m; C++){
		for(int p=0; p<n; p++){
			ll tmp=(ll)(A[n+p-1].x+0.5);
			ll test=0;
			for(int i=0; i<n; i++)
				test += xx[i] * yy[(i+p)%n];
			tmp = -2 * tmp;
			tmp += sumxifang + (ll)2 * C * sumxi + (ll)C * C * n;
			tmp -= (ll)2 * C * sumyi;
			tmp += sumyifang;
			ans = min(ans, tmp);
		}
	}
	cout<<ans<<endl;
	return 0;
}
原文地址:https://www.cnblogs.com/poorpool/p/8821163.html