---
# System prepended metadata

title: '[CODEFORCES 1301B Motarack''s Birthday](https://codeforces.com/contest/1301/problem/B)'

---

[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;
}
```