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 }
posted @ 2018-03-09 07:31  阿波罗2003  阅读(...)  评论(... 编辑 收藏