題意
題目連結
Sol
很nice的決策單調性題目
首先把給出的式子移項,我們要求的$P_i = max(a_j + sqrt{|i – j|}) – a_i$。
按套路把絕對值拆掉,$p_i = max(max_{j = 1}^i (a_j = sqrt{i – j}), max_{j = i + 1}^n (a_j + sqrt{j – i})) – a_i$
對於後面的一段,我們把序列翻轉之後和前一段是等價的。
也就是說,我們現在只需要找到$P_i = max_{j = 1}^i (a_j + sqrt{i – j})$
考慮到式子中只有一個max函式,那這玩意兒應該是有決策單調性的
直接設$f_j = a_j + sqrt{i – j}, i geqslant j$,其中$i$是自變數
觀察這個函式,應該是一個在$[j, INF]$內有定義,過點$(j, a[j])$的函式,且增速與函式$g_i = sqrt{i}$相同
我們需要做的,就是對每個$i$,找到最大的$f_j$
考慮到$g_i$增長速度會越來越慢,所以一個函式增長到一定程度後可能會被另一個函式取代
直接用單調佇列維護,設$K_{i, j}$表示$f_i, f_j$的交點,$h, t$分別表示隊首/尾,
當新加入一個元素$i$的時候,顯然,若$K_{t -1, t} > K_{t – 1, i}$,那麼$t$這個函式是沒用的、
當$K_{h, h+1} < i$的時候,彈出隊首
就是最後輸出答案的時候有點“卡精度”,真噁心
經驗:
以後看到$f_i = max(f_j) + g$的式子一定要往單調性上想,如果單調性不是很顯然的話可以用換元法設函式找單調性
另外絕對值拆開算一般會好算一些
#include<bits/stdc++.h> #define Pair pair<int, int> #define MP(x, y) make_pair(x, y) #define fi first #define se second using namespace std; const int MAXN = 1e6 + 10, INF = 1e9 + 10; inline int read() { char c = getchar(); int x = 0, f = 1; while(c < `0` || c > `9`) {if(c == `-`) f = -1; c = getchar();} while(c >= `0` && c <= `9`) x = x * 10 + c - `0`, c = getchar(); return x * f; } int N, a[MAXN], q[MAXN], Cro[MAXN]; double P[MAXN], sqr[MAXN]; double calc(int j, int i) { return a[j] + sqr[i - j]; } int K(int x, int y) { int l = max(x, y), r = N, ans = N + 1; while(l <= r) { int mid = l + r >> 1; if(calc(x, mid) >= calc(y, mid)) l = mid + 1; else r = mid - 1, ans = mid; } return ans; } void solve() { int h = 1, t = 0; for(int i = 1; i <= N; i++) { while(h < t && K(q[t - 1], q[t]) >= K(q[t], i)) t--; q[++t] = i; while(h < t && K(q[h], q[h + 1]) <= i) h++; P[i] = max(P[i], calc(q[h], i)); } } main() { N = read(); for(int i = 1; i <= N; i++) a[i] = read(), sqr[i] = sqrt(i); solve(); reverse(a + 1, a + N + 1); reverse(P + 1, P + N + 1); solve(); for(int i = N; i >= 1; i--) printf("%d ", max(0, (int)ceil(P[i]) - a[i])); return 0; }