bzoj 2216 Lightning Conductor - 二分法 - 动态规划
题目传送门
需要root权限的传送门
题目大意
给定一个长度为$n$的数组,要求对每个$1 \leqslant i \leqslant n$找到最小整数的$p$,对于任意$j$满足使得$a_{i} + p - \sqrt{\left | i - j \right |} \geqslant a_{j}$。
一来想到函数$y = \left \lceil \sqrt{x} \right \rceil$,至多有根号个取值,然后发现$O(n\sqrt{n})$会稳T。
对于函数$y = \sqrt{x}$有一些很优美的性质,比如它的增长率不断递减(因为它的导数$y' = \frac{1}{\sqrt{x}}$,$y'$随$x$减小而减小)。
所以对于两个决策点$i, j$,若满足$i < j$,如果它们在转移到$p_{k}$的时候$i$没有$j$优,那么$i$不会比$j$优了。
同样的,如果$i$还是比$j$优,那么在$k$之前还是这样的。
因此决策点是单调的。
所以我们可以用整体二分的写法。
每次考虑$f[mid]$的函数值,找到它的最优决策点$pos$,那么可以确定左区间的决策点的范围,对于右区间同理。
Code
1 /** 2 * bzoj 3 * Problem#2216 4 * Accepted 5 * Time: 4516ms 6 * Memory: 13032k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 12 int n; 13 int *csqr; 14 int *ar; 15 int *f, *g; 16 17 inline void init() { 18 scanf("%d", &n); 19 csqr = new int[(n + 1)]; 20 ar = new int[(n + 1)]; 21 f = new int[(n + 1)]; 22 g = new int[(n + 1)]; 23 for (int i = 1; i <= n; i++) 24 scanf("%d", ar + i); 25 } 26 27 double *sqs; 28 void prepare() { 29 sqs = new double[(n + 1)]; 30 sqs[0] = 0; 31 for (int i = 1; i <= n; i++) 32 sqs[i] = sqrt(i); 33 } 34 35 void dividing(int* f, int l, int r, int ql, int qr) { 36 if (l > r) return; 37 int mid = (l + r) >> 1, pos; 38 double mx = 0.0, cmp; 39 for (int i = ql; i <= qr && i <= mid; i++) 40 if ((cmp = ar[i] + sqs[mid - i]) > mx) 41 mx = cmp, pos = i; 42 f[mid] = ceil(mx - ar[mid]); 43 dividing(f, l, mid - 1, ql, pos); 44 dividing(f, mid + 1, r, pos, qr); 45 } 46 47 inline void solve() { 48 dividing(f, 1, n, 1, n); 49 reverse(ar + 1, ar + n + 1); 50 dividing(g, 1, n, 1, n); 51 for (int i = 1; i <= n; i++) 52 printf("%d\n", max(f[i], g[n - i + 1])); 53 } 54 55 int main() { 56 init(); 57 prepare(); 58 solve(); 59 return 0; 60 }