P3723 [AH2017/HNOI2017]礼物

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

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

在将两个手环旋转且装饰物对齐了之后,从对齐的某个位置开始逆时针方向对装饰物编号(1,2,…,n),其中 (n) 为每个手环的装饰物个数, 第 (1) 个手环的 (i) 号位置装饰物亮度为 (xi),第 (2) 个手环的 (i) 号位置装饰物亮度为 (yi),两个手环之间的差异值为(参见输入输出样例和样例解释):

$$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)


很有意思的一个题目,关键是要想到怎么把题目变成推式子,如果分析失误就会像我一样当成(DP)+二分使劲写写不出来。

处理之前,我们要先把原式中的两个变量固定一个——即假设只有(B)会旋转,并把(B)的亮度整体增加整数(C)(使(C)可正可负即可。这样式子就变成了

$$sum(x_i + y_i + k) ^ 2$$

(先暂时忽略(x_i)(y_i)的顺序问题。)

紧接着我们把式子展开,得到:

$$sum x_i^2 + sum y_i^2 + N * K ^ 2 + 2 * K * (sum x_i -y_i) - 2 * sum x_i * y_i$$

前两项是常数项,中间两项用二次函数代入对称轴求最小值,最后一项我们把(y)数列翻转倍长(翻转是便于卷积,倍长是为了可以忽略顺序问题,断环为链。)

这样如果(y_i)要逆时针转(k)位,那么最后一项就会变成(2 * sum x_i * y_{i + k})。也就是说,倍长处理后,我们只需在(N + 1) -> (N * 2)位找一个最大的值,就是手链顺序对答案产生的最大贡献了。

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

#define LL long long
const int N = 100010;
const double pi = acos (-1);

struct Complex {
	double x, y;
	Complex (double xx = 0, double yy = 0) {x = xx, y = yy;}
	Complex operator + (Complex rhs) {return Complex (x + rhs.x, y + rhs.y);}
	Complex operator - (Complex rhs) {return Complex (x - rhs.x, y - rhs.y);}
	Complex operator * (Complex rhs) {return Complex (x * rhs.x - y * rhs.y, x * rhs.y + y * rhs.x);}
}X[N], Y[N], S[N];

int n, m, l, lim = 1, x[N], y[N], r[N]; LL ans, res;

void fast_fast_tle (Complex *A, int type) {
	for (int i = 0; i < lim; ++i) {
		if (i < r[i]) {
			swap (A[i], A[r[i]]);
		}
	}
	for (int mid = 1; mid < lim; mid <<= 1) {
		Complex Wn (cos (pi / mid), type * sin (pi / mid));
		for (int p = 0; p < lim; p += (mid << 1)) {
			Complex w (1, 0);
			for (int k = 0; k < mid; ++k, w = w * Wn) {
				Complex xx = A[p + k];
				Complex yy = w * A[p + k + mid];
				A[p + k] = xx + yy;
				A[p + k + mid] = xx - yy;
			}
		}
	}
	if (type == -1) {
		for (int i = 0; i < lim; ++i) {
			A[i].x /= lim;
		}
	}
}

int main () {
	cin >> n >> m;
	for (int i = 1; i <= n; ++i) cin >> x[i];
	for (int i = 1; i <= n; ++i) cin >> y[i];
	for (int i = 1; i <= n; ++i) {
		ans += x[i] * x[i];
		ans += y[i] * y[i];
	    res += y[i] - x[i];
	}
	int T1 = ceil  ((double) res / (double) n);
	int T2 = floor ((double) res / (double) n);
	ans += min (
				n * T1 * T1 - 2 * T1 * res,
				n * T2 * T2 - 2 * T2 * res
				);
	n = n - 1;
	for (int i = 0; i <= n; ++i) {
		X[i].x = x[i + 1];
		Y[i].x = y[n - i + 1];
	}
	for (int i = n + 1; i <= n * 2 + 1; ++i) {
		Y[i] = Y[i - n - 1];
	}
	while (lim <= n * 2 + 1) ++l, lim <<= 1;
	for (int i = 0; i < lim; ++i) {
		r[i] = (r[i >> 1] >> 1) | ((i & 1) * (lim >> 1));
	}
	fast_fast_tle (X, 1);
	fast_fast_tle (Y, 1);
	for (int i = 0; i < lim; ++i) {
		S[i] = X[i] * Y[i];
	}
	fast_fast_tle (S, -1);
	LL tmp = (LL) -1e18;
	for (int i = n + 1; i <= n * 2 + 1; ++i) {
		tmp = max (tmp, (LL) (S[i].x + 0.5));
	}
	cout << ans - tmp * 2 << endl;
}

原文地址:https://www.cnblogs.com/maomao9173/p/10429000.html