洛谷传送门
BZOJ传送门
题目描述
我的室友最近喜欢上了一个可爱的小女生。马上就要到她的生日了,他决定买一对情侣手环,一个留给自己,一个送给她。每个手环上各有 个装饰物,并且每个装饰物都有一定的亮度。
但是在她生日的前一天,我的室友突然发现他好像拿错了一个手环,而且已经没时间去更换它了!他只能使用一种特殊的方法,将其中一个手环中所有装饰物的亮度增加一个相同的自然数 (即非负整数)。并且由于这个手环是一个圆,可以以任意的角度旋转它,但是由于上面装饰物的方向是固定的,所以手环不能翻转。需要在经过亮度改造和旋转之后,使得两个手环的差异值最小。
在将两个手环旋转且装饰物对齐了之后,从对齐的某个位置开始逆时针方向对装饰物编号 ,其中 为每个手环的装饰物个数, 第 个手环的 号位置装饰物亮度为 ,第 个手环的 号位置装饰物亮度为 ,两个手环之间的差异值为(参见输入输出样例和样例解释):
麻烦你帮他计算一下,进行调整(亮度改造和旋转),使得两个手环之间的差异值最小,这个最小值是多少呢?
输入输出格式
输入格式:
输入数据的第一行有两个数n, m,代表每条手环的装饰物的数量为n,每个装饰物的初始亮度小于等于m。
接下来两行,每行各有n个数,分别代表第一条手环和第二条手环上从某个位置开始逆时针方向上各装饰物的亮度。
输出格式:
输出一个数,表示两个手环能产生的最小差异值。注意在将手环改造之后,装饰物的亮度可以大于 。
输入输出样例
输入样例#1:
5 6
1 2 3 4 5
6 3 3 4 5
输出样例#1:
1
说明
【样例解释】
需要将第一个手环的亮度增加 ,第一个手环的亮度变为:
旋转一下第二个手环。对于该样例,是将第二个手环的亮度 向左循环移动一个位置,使得第二手环的最终的亮度为: 。
此时两个手环的亮度差异值为
【数据范围】
30%的数据满足 ;
70%的数据满足 ;
100%的数据满足 。
解题分析
我们首先来推一波式子:
我们求的是
拆开来看:
前面一大堆是一个二次函数的形式, 可以直接求出在对称轴处的最优值。
后面那一堆说白了就是考虑如何调整 与 的对齐方式。然后我们就可以复制、倍长 或 数组, FFT求一遍卷积, 在 范围内寻找最大值即可。
(《算法导论》和网上许多dalao博客已经把FFT写的十分详细, 所以蒟蒻就不在这里献丑了)
代码如下:
#include <cstdio>
#include <cstring>
#include <cctype>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#include <limits.h>
#define R register
#define IN inline
#define gc getchar()
#define W while
#define MX 400050
#define db double
#define ll long long
template <class T>
IN void in(T &x)
{
x = 0; R char c = gc;
W (!isdigit(c)) c = gc;
W (isdigit(c))
x = (x << 1) + (x << 3) + c - 48, c = gc;
}
const db PI = std::acos((db)-1);
int len, mx, tot, lg;
ll ans[MX], sgm1, sgm2, res;
int c[MX], d[MX], rev[MX];
struct Complex {db re, im;} a[MX], b[MX];
IN Complex operator + (const Complex &x, const Complex &y) {return {x.re + y.re, x.im + y.im};}
IN Complex operator - (const Complex &x, const Complex &y) {return {x.re - y.re, x.im - y.im};}
IN Complex operator * (const Complex &x, const Complex &y) {return {x.re * y.re - x.im * y.im, x.re * y.im + x.im * y.re};}
IN void FFT(Complex *dat, const int &typ)
{
R int now, cur, step, seg, bd;
Complex deal, base, tar1, tar2;
for (now = 0; now < tot; ++now) if(now < rev[now]) std::swap(dat[rev[now]], dat[now]);
for (seg = 1; seg < tot; seg <<= 1)
{
base = {std::cos(PI / seg), typ * std::sin(PI / seg)}; step = seg << 1;
for (now = 0; now < tot; now += step)
{
bd = now + seg; deal = {1, 0};
for (cur = now; cur < bd; ++cur, deal = deal * base)
{
tar1 = dat[cur], tar2 = dat[cur + seg] * deal;
dat[cur] = tar1 + tar2, dat[cur + seg] = tar1 - tar2;
}
}
}
}
int main(void)
{
in(len), in(mx); int n;
int bd; bd = len << 1; tot = 1, n = len * 3;
for (R int i = 1; i <= len; ++i) in(c[i]), sgm1 += c[i], res += c[i] * c[i];
for (R int i = 1; i <= len; ++i) in(d[i]), sgm2 += d[i], res += d[i] * d[i];
int mid = round(1.0 * (sgm2 - sgm1) / len);
res += len * mid * mid + 2 * mid * (sgm1 - sgm2);
for (R int i = 0; i < len; ++i) a[i] = {c[len - i], 0};
for (R int i = len; i < bd; ++i) a[i] = a[i - len];
for (R int i = 0; i < len; ++i) b[i] = {d[i + 1], 0};
W (tot <= n) tot <<= 1, ++lg;
for (R int i = 0; i < tot; ++i) rev[i] = (rev[i >> 1] >> 1) | ((i & 1) << (lg - 1));
FFT(a, 1), FFT(b, 1);
for (R int i = 0; i < tot; ++i) a[i] = a[i] * b[i];
FFT(a, -1); bd = (len << 1) - 1; ll ret = -LONG_LONG_MAX;
for (R int i = len - 1; i <= bd; ++i) ret = std::max(ret, (ll)(a[i].re / tot + 0.5));
printf("%lld", res - (ret << 1));
}