P3723 [AH2017/HNOI2017]礼物
题目链接:[AH2017/HNOI2017]礼物
题意:
两个环x, y 长度都为n
k可取 0 ~ n - 1 c可取任意值
求 ∑ ( x[i] - y[(i + k) % n + 1] + c) ^ 2 的最小值
ans[k] = ∑ ( x[i], y[(i + k) % n + 1] ) ^ 2
拆项
发现ans[k] = ∑ x[i] ^ 2 + ∑ y[i] ^ 2 + n * c ^ 2 + 2 * ∑ x[i] * c - 2 * ∑ y[i] * c - 2 * ∑ x[i] * y[(i + k) % n + 1]
然后 就没有然后了
暴力一项一项解 处理出所有的ans 找最小值就行了
∑ x[i] ^ 2 + ∑ y[i] ^ 2
固定的直接算就行
n * c ^ 2 + 2 * ∑ x[i] * c- 2 * ∑ y[i] * c
把这个式子看作是关于c的
动用初中数学二次函数知识算出最小值
- 2 * ∑ x[i] * y[(i + k) % n + 1]
那么这个呢? FFT大法好
这就很板子
然后是代码
1 // luogu-judger-enable-o2 2 #include <cstdio> 3 #include <algorithm> 4 #include <cmath> 5 #include <complex> 6 using namespace std; 7 const int N = 3e5 + 5; 8 const double pi = acos(-1); 9 typedef complex<double> cd; 10 cd a[N], b[N]; 11 double f[N]; 12 int n, m, lim, l, r[N]; 13 int x[N], y[N], sx, sy; 14 long long ans; 15 16 inline void init(){ 17 scanf("%d%d", &n, &m); 18 for(int i = 0; i < n; i++){ 19 scanf("%d", &x[i]); 20 a[i] = x[i], ans += x[i] * x[i], sx += x[i]; 21 } 22 for(int i = 0; i < n; i++){ 23 scanf("%d", &y[i]); 24 b[i] = y[i], ans += y[i] * y[i], sy += y[i]; 25 } 26 27 reverse(a, a + n); //翻转a 28 for(int i = 0; i < n; i++) b[i + n] = b[i]; 29 for(lim = 1; lim < 3 * n; lim <<= 1) l++;//注意是3 * n哦 30 } 31 32 inline void cal(){ 33 for(int i = 0; i < lim; i++) 34 r[i] = (r[i >> 1] >> 1) | ((i & 1) << (l - 1)); 35 } 36 37 void fft(cd * c, int type){ 38 for(int i = 0; i < lim; i++) 39 if(i < r[i]) swap(c[i], c[r[i]]); 40 for(int i = 1; i < lim; i <<= 1){ 41 cd xx(cos(pi / i), type * sin(pi / i)); 42 for(int j = 0; j < lim; j += (i << 1)){ 43 cd yy(1, 0); 44 for(int k = 0; k < i; k++, yy *= xx){ 45 cd p = c[j + k], q = yy * c[i + j + k]; 46 c[j + k] = p + q; 47 c[i + j + k] = p - q; 48 } 49 } 50 } 51 } 52 53 int main(){ 54 init(); 55 cal(); 56 int as1 = floor(1.0 * (sy - sx) / n), as2 = ceil(1.0 * (sy - sx) / n); 57 ans += min(n * as1 * as1 + 2 * (sx - sy) * as1, n * as2 * as2 + 2 * (sx - sy) * as2); 58 fft(a, 1); fft(b, 1); 59 for(int i = 0; i < lim; i++) a[i] *= b[i]; 60 fft(a, -1); 61 for(int i = n - 1; i < (n << 1) - 1; i++)//统计答案 f[n - 1 + i] = ans[i]; 62 f[i] = round(a[i].real() / lim); 63 ans -= *max_element(f + n - 1, f + (n << 1) - 1) * 2; 64 printf("%lld", ans); 65 return 0; 66 }