[AH2017/HNOI2017]禮物
阿新 • • 發佈:2018-12-10
嘟嘟嘟
感覺fft的題重點在於推式子……
因為\(n \leqslant 5e4, m \leqslant 100\),所以可以列舉旋轉的位置和增加的亮度,然後想辦法在\(O(1)\)時間內得到答案。
令列舉到第\(i\)個位置時\(A, B\)兩個手環的序列為\(A_i, B_i\),此時\(B_i\)整體怎加了\(x\)亮度,則:
\[ans = \sum_{i = 0} ^ {n - 1} (a_i - b_i - x) ^ 2\]
然後拆開\((a_i - b_i - x) ^ 2\):
\({a_i} ^ 2 + {b_i} ^ 2 + x ^ 2 - 2a_i b_i - 2a_i x + 2b_i x\)
於是
\[ans = \sum_{i = 0} ^ {n - 1} {a_i} ^ 2 + \sum_{i = 0} ^ {n - 1} {b_i} ^ 2 + nx ^ 2 - 2x(\sum_{i = 0} ^ {n - 1} b_i - \sum_{i = 0} ^ {n - 1} a_i) - 2 \sum_{i = 0} ^ {n - 1} a_i b_i\]
然後就發現只有最後一項是不知道的,而且只要把\(A\)翻轉一下,就可以卷積了!
但是如果在每一次列舉都求一遍卷積,\(O(mn ^ 2 \log{n})\)時間肯定撐不住,而且求的時候也有很多重複的部分。
因此,我們可以斷環為鏈,得到一個翻轉後的長度為\(2n\)
列舉到第\(i\)個位置的時候,根據卷積下標相加,\(c(n + i)\)就是答案。
#include<cstdio> #include<iostream> #include<cmath> #include<algorithm> #include<cstring> #include<cstdlib> #include<cctype> #include<vector> #include<stack> #include<queue> using namespace std; #define enter puts("") #define space putchar(' ') #define Mem(a, x) memset(a, x, sizeof(a)) #define rg register typedef long long ll; typedef double db; const int INF = 0x3f3f3f3f; const db eps = 1e-8; const int maxn = 8e6 + 5; const db PI = acos(-1); inline ll read() { ll ans = 0; char ch = getchar(), last = ' '; while(!isdigit(ch)) last = ch, ch = getchar(); while(isdigit(ch)) ans = (ans << 1) + (ans << 3) + ch - '0', ch = getchar(); if(last == '-') ans = -ans; return ans; } inline void write(ll x) { if(x < 0) x = -x, putchar('-'); if(x >= 10) write(x / 10); putchar(x % 10 + '0'); } int n, m, len = 1; ll a2 = 0, b2 = 0, suma = 0, sumb = 0; ll Max = 0, ans = INF; struct Comp { db x, y; inline Comp operator + (const Comp& oth)const { return (Comp){x + oth.x, y + oth.y}; } inline Comp operator - (const Comp& oth)const { return (Comp){x - oth.x, y - oth.y}; } inline Comp operator * (const Comp& oth)const { return (Comp){x * oth.x - y * oth.y, x * oth.y + oth.x * y}; } inline friend void swap(Comp& a, Comp& b) { swap(a.x, b.x); swap(a.y, b.y); } }a[maxn], b[maxn], omg[maxn], inv[maxn]; void init() { omg[0] = inv[0] = (Comp){1, 0}; omg[1] = inv[len - 1] = (Comp){cos(2 * PI / len), sin(2 * PI / len)}; for(int i = 2; i < len; ++i) omg[i] = inv[len - i] = omg[i - 1] * omg[1]; } void fft(Comp* a, Comp* omg) { int lim = 0; while((1 << lim) < len) lim++; for(int i = 0; i < len; ++i) { int t = 0; for(int j = 0; j < lim; ++j) if((i >> j) & 1) t |= (1 << (lim - j - 1)); if(i < t) swap(a[i], a[t]); } for(int l = 2; l <= len; l <<= 1) { int q = l >> 1; for(Comp* p = a; p != a + len; p += l) for(int i = 0; i < q; ++i) { Comp t = omg[len / l * i] * p[i + q]; p[i + q] = p[i] - t, p[i] = p[i] + t; } } } int main() { n = read(); m = read(); for(int i = 0; i < n; ++i) { db x = read(); a[i] = a[n + i] = (Comp){x, 0}; a2 += x * x; suma += x; } for(int i = 0; i < n; ++i) { db x = read(); b[n - i - 1] = (Comp){x, 0}; b2 += x * x; sumb += x; } while(len < (n << 1) + n) len <<= 1; init(); fft(a, omg); fft(b, omg); for(int i = 0; i < len; ++i) a[i] = a[i] * b[i]; fft(a, inv); for(int i = 0; i < len; ++i) a[i].x = (ll)(a[i].x / len + 0.5); for(int i = 0; i < n; ++i) for(int j = -m; j <= m; ++j) ans = min(ans, a2 + b2 + (ll)n * j * j - (((sumb - suma) * j) << 1) - ((ll)a[i + n].x << 1)); write(ans), enter; return 0; }