[AH2017/HNOI2017]礼物

题目描述

我的室友最近喜欢上了一个可爱的小女生。马上就要到她的生日了,他决定买一对情侣手环,一个留给自己,一个送给她。每个手环上各有 n 个装饰物,并且每个装饰物都有一定的亮度。

但是在她生日的前一天,我的室友突然发现他好像拿错了一个手环,而且已经没时间去更换它了!他只能使用一种特殊的方法,将其中一个手环中所有装饰物的亮度增加一个相同的自然数 c(即非负整数)。并且由于这个手环是一个圆,可以以任意的角度旋转它,但是由于上面装饰物的方向是固定的,所以手环不能翻转。需要在经过亮度改造和旋转之后,使得两个手环的差异值最小。

在将两个手环旋转且装饰物对齐了之后,从对齐的某个位置开始逆时针方向对装饰物编号1,2,…,n,其中 n 为每个手环的装饰物个数, 第 1 个手环的 i 号位置装饰物亮度为 x[i],第 2 个手环的 i 号位置装饰物亮度为 y[i],两个手环之间的差异值为 (sum_{i=1}^{n}{(x[i]-y[i])^2})

麻烦你帮他计算一下,进行调整(亮度改造和旋转),使得两个手环之间的差异值最小,这个最小值是多少呢?

输入输出格式

输入格式:

输入数据的第一行有两个数n, m,代表每条手环的装饰物的数量为n,每个装饰物的初始亮度小于等于m。

接下来两行,每行各有n个数,分别代表第一条手环和第二条手环上从某个位置开始逆时针方向上各装饰物的亮度。

输出格式:

输出一个数,表示两个手环能产生的最小差异值。注意在将手环改造之后,装饰物的亮度

可以大于 m。

输入输出样例

输入样例#1:

5 6
1 2 3 4 5
6 3 3 4 5

输出样例#1:

1

说明

样例解释

需要将第一个手环的亮度增加1,第一个手环的亮度变为: 2 3 4 5 6

旋转一下第二个手环。对于该样例,是将第二个手环的亮度6 3 3 4 5向左循环移动一个位置,使得第二手环的最终的亮度为: 3 3 4 5 6。

此时两个手环的亮度差异值为1

数据范围

30%的数据满足n≤500, m≤10;

70%的数据满足n≤5000;

100%的数据满足1≤n≤50000, 1≤m≤100, 1≤ai≤m。


一开始没看出来是FFT

好像不是很难 比力那题好弄多了

根据题意,就是让你求(sum_{i=1}^{n}{(x[i]-y[i]+c)^2})

那个c就是增加的亮度

先化一波式子

(sum_{i=1}^{n}{(x[i]-y[i]+c)^2})

(sum_{i=1}^{n}{x[i]^2+y[i]^2-2*c*(x[i]-y[i])+c^2+2*x[i]*y[i]})

然后我们发现第一和第二项都可以直接求

然后两项是个二次函数,因为c很小,直接枚举c也可以求(当然也可以用公式求,但好像会被卡精度)

现在我们只需要考虑最后一项 (-2sum_{i=1}^{n}{x[i]*y[i]})

因为礼物是可以随意转动的,所以式子其实应该是这样:(-2sum_{i=1}^{n}{x[i]*y[i+j]})

(j)就是转了多少次

然后我们就把x[i]翻转过来

式子就成了(-2sum_{i=1}^{n}{x[n-i+1]*y[i+j]})

然后就破环为链倍长y数组

把x和y卷积一下取(n+1)(n*2)项的最大值乘上-2再加上前面求的那些东西就好辣

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
# define LL long long
using namespace std ;
const int M = 400005 ;
const LL INF = 1e17 ;
const double Pi = acos(-1.0) ;
inline int read() {
	char c = getchar() ; int x = 0 , w = 1 ;
	while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ;}
	while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
	return x*w ;
}
struct Complex {
	double x , y ;
	Complex (double Tx = 0 , double Ty = 0) { x = Tx ; y = Ty ; }
}a[M] , b[M];
Complex operator + (Complex a , Complex b) {
	return Complex (a.x + b.x , a.y + b.y) ;
}
Complex operator - (Complex a , Complex b) {
	return Complex (a.x - b.x , a.y - b.y) ;
}
Complex operator * (Complex a , Complex b) {
	return Complex (a.x * b.x - a.y * b.y , a.x * b.y + a.y * b.x) ;
}
int n , m ;
int x[M] , y[M] , c ;
LL sumx , sumy , sumx2 , sumy2 , tot = INF , Ans = INF ;
int l = 0 , digital = 1 , r[M] ;
inline void FFT(Complex *A , int unit) {
	for(int i = 0 ;  i < digital ; i ++)
	  if(r[i] > i)
	    swap(A[i] , A[r[i]]) ;
    for(int mid = 1 ; mid < digital ; (mid <<= 1)) {
    	Complex W (cos(Pi / mid) , unit * sin(Pi / mid)) ;
    	int R = (mid << 1) ;
    	for(int j = 0 ; j < digital ; j += R) {
    		Complex w (1 , 0) ;
    		for(int k = 0 ; k < mid ; k ++ , w = w * W) {
    			Complex x = A[j + k] , y = w * A[j + k + mid] ;
    			A[j + k] = x + y ; A[j + k + mid] = x - y ;
			}
		}
	}
}
inline void Solve() {
	while(digital <= n + m) digital <<= 1 , ++l ;
	for(int i = 0 ; i < digital ; i ++)
	  r[i] = (r[i>>1]>>1) | ((i&1)<<(l - 1)) ;
	FFT(a , 1) ; FFT(b , 1) ;
	for(int i = 0 ; i <= digital ; i ++) a[i] = a[i] * b[i] ;
	FFT(a , -1) ;
}
int main() {
	n = read() ; int upp = read() ;
	for(int i = 1 ; i <= n ; i ++) {
	    x[i] = read() ;
	    sumx += x[i] ; sumx2 += x[i] * x[i] ;
	}
	for(int i = 1 ; i <= n ; i ++) {
	    y[i] = read() ;
	    sumy += y[i] ; sumy2 += y[i] * y[i] ;
	}
	for(int i = -upp ; i <= upp ; i ++)
		tot = min(tot , n * i * i + 2 * (sumx - sumy) * i) ;
    tot += sumx2 + sumy2 ;
	m = n + n ;
	for(int i = 1 ; i <= n ; i ++) a[i].x = x[n - i + 1] ;
	for(int i = 1 ; i <= n ; i ++) b[i].x = y[i] ;
	for(int i = n + 1 ; i <= m ; i ++) b[i].x = y[i - n] ;
	Solve() ;
	LL temp = -INF ;
	for(int i = n + 1 ; i <= m ; i ++)
	  temp = max(temp , (LL)(a[i].x / digital + 0.5)) ;
	Ans = tot - 2 * temp ;
    printf("%lld
",Ans) ;
    return 0 ;
}
原文地址:https://www.cnblogs.com/beretty/p/9522656.html