【每日题解# 13】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 } 
View Code

猜你喜欢

转载自www.cnblogs.com/hjmmm/p/9416090.html