[CODEFORCES 1301B Motarack's Birthday](https://codeforces.com/contest/1301/problem/B) = ## 題目大意 給定 $a$ 非負整數列,但有些值為 $-1$ 需將 $-1$ 替換為非負整數 $k$ 使得 $m$ 最小 $m$ 為所有相鄰數的絕對差中最大值 例如 $a = (a_1, a_2, a_3)$ 則 $m = \max(|a_1-a_2|, |a_2-a_3|)$ ## 輸入 $a$ 數列 ## 輸出 $m$ 及 $k$ (若 $k$ 有多個,任意輸出一個就好) ## 解法 1 觀察當 $a$ 長度為 $3$ 的情況 (因為足夠小 以便摸索解法) 如 $a = (x, -1, y)$ 不失一般性的設 $x < y$ 1. 若 $k \le x$ 則 $m \ge y-x$ 2. 若 $k \ge y$ 則 $m \ge y-x$ 3. 若 $x < k < y$ 則 $m = \max(k-x, y-k) < y-x$ 所以要將 $k$ 落在 3. 的條件下才使 $m$ 盡量小 再細看 $x < k < y$ 從 $k = x+1$ 遞增至 $y-1$ 會發現 $k$ 與 $x$ 絕對差越來越大,而與 $y$ 絕對差越來越小 在這之中 $k$ 與 $x, y$ 的兩個絕對差會逐步至一個**最小對**,接著再增大 > $m$ 則從這對中找最大的那個 反之從 $k = y-1$ 遞減至 $x+1$ 也一樣 這貌似是個類似二次函數的凸函數 ($f: k \mapsto m$) (只有一個**局部極值**) 若是能證明任意長度的 $a$ 都有此函數特性,那麼可以用三分搜解決 不用管**不與 $k$ 相鄰的絕對差**, 因為不與 $k$ 相鄰的絕對差之中的最大值 $t$ 永遠都不會變動,也就是說若函數擁有理想上的特性,那麼 $m$ 在遞減時也只會降到 $t$。 而**與 $k$ 相鄰的絕對差**,也只需要關心最大值 $y$ 與最小值 $x$ 因為若存在介於 $x, y$ 的數 $z$ 使得 $|k-z| > \max(|k-y|, |k-x|)$ 則若 $k \ge z$ 得 $k > x$ (因 $x$ 是最小值),但 $k-z > k-x \implies z < x$ 矛盾 或若 $k \le z$ 得 $k < y$ (因 $y$ 是最大值),但 $z-k > y-k \implies z > y$ 矛盾 > $|k-z|$ 帶絕對值,所以要拆兩狀況論證 也就是說,任意長度的 $a$ 可類比為長度 $3$ 的情況 ```cpp #include<bits/stdc++.h> using namespace std; int const maxn = 1e5 + 10; int t, n; vector<int> a; int m(int k) { vector<int> b(a); int mx = 0; // maximum of absolute differences for(int i = 0; i+1 < n; i++) { if(b[ i ] == -1) b[ i ] = k; if(b[i+1] == -1) b[i+1] = k; mx = max(mx, abs(b[i] - b[i+1])); } return mx; } int main() { scanf("%d", &t); while(t--) { scanf("%d", &n); a.resize(n); for(int i = 0; i < n; i++) scanf("%d", &a[i]); int l = 0, r = 1e9+10; while(r - l > 2) { int k1 = l + (r-l)/3, k2 = r - (r-l)/3; if(m(k1) > m(k2)) l = k1; else r = k2; } if(m(l) < m(l+1)) printf("%d %d\n", m(l), l); else printf("%d %d\n", m(l+1), l+1); } return 0; } ``` ## 解法 2 將題目稍微**變形**一下 不去看相鄰對,而是給定 $a$ 非負數列 (不含 $-1$),找到 $k$ 使每個數與 $k$ 的差中最大值最小化 明顯的,$k = {\min(a)+\max(a)\over2}$ > 解法 1 沒有給出 $k$ 應為多少 回到**原問題**,就是要把 $-1$ 相鄰的非負數抓出來算出 $k$ ```cpp #include<bits/stdc++.h> using namespace std; int const maxn = 1e5 + 10; int t, n, a[maxn]; int main() { scanf("%d", &t); while(t--) { scanf("%d", &n); for(int i = 0; i < n; i++) scanf("%d", &a[i]); int mx = 0, mi = 1e9; for(int i = 0; i < n; i++) { bool c1 = i > 0 && a[i-1] == -1 && a[i] != -1; bool c2 = i < n-1 && a[i+1] == -1 && a[i] != -1; if(c1 || c2) mx = max(mx, a[i]), mi = min(mi, a[i]); } int m = 0, k = (mx+mi)/2; for(int i = 0; i < n; i++) if(a[i] == -1) a[i] = k; for(int i = 0; i+1 < n; i++) m = max(m, abs(a[i]-a[i+1])); printf("%d %d\n", m, k); } return 0; } ```