「JSOI2016」灯塔

「JSOI2016」灯塔

传送门

我们先只计算照亮左边的灯塔的最低高度,计算右边的类同,然后只要取 \(\max\) 就好了。

那么稍微整理一下式子:\(p_i \ge h_j - h_i + \sqrt{i - j}\)

我们发现可以对 \(j\) 数论分块,然后每次查询块内最大的 \(h_j\) 即可。

区间最大值用 \(\text{ST}\) 表维护。

复杂度就是 \(O(n \log n + n\sqrt{n})\)

#include <cstdio>
#define rg register
#define file(x) freopen(x".in", "r", stdin), freopen(x".out", "w", stdout)
template < class T > inline T max(T a, T b) { return a > b ? a : b; }
template < class T > inline T min(T a, T b) { return a < b ? a : b; }
template < class T > inline void read(T& s) {
    s = 0; int f = 0; char c = getchar();
    while ('0' > c || c > '9') f |= c == '-', c = getchar();
    while ('0' <= c && c <= '9') s = s * 10 + c - 48, c = getchar();
    s = f ? -s : s;
}

const int _ = 1e5 + 5;
int n, lg[_], mx[18][_];

inline int query(int l, int r) {
    int x = lg[r - l + 1];
    return max(mx[x][l], mx[x][r - (1 << x) + 1]);
}

int main() {
#ifndef ONLINE_JUDGE
    file("cpp");
#endif
    read(n);
    for (rg int i = 1; i <= n; ++i) read(mx[0][i]);
    for (rg int i = 2; i <= n; ++i) lg[i] = lg[i >> 1] + 1;
    for (rg int i = 1; i <= lg[n]; ++i)
        for (rg int j = 1; j + (1 << i) - 1 <= n; ++j)
    	    mx[i][j] = max(mx[i - 1][j], mx[i - 1][j + (1 << (i - 1))]);
    for (rg int ans = 0, i = 1; i <= n; ++i, ans = 0) {
    	for (rg int j = 1, l = i + 1, r; l <= n; ++j, l = r + 1)
	    r = min(n, i + j * j), ans = max(ans, query(l, r) - mx[0][i] + j);
	for (rg int j = 1, r = i - 1, l; r >= 1; ++j, r = l - 1)
    	    l = max(1, i - j * j), ans = max(ans, query(l, r) - mx[0][i] + j);
    	printf("%d\n", ans);
    }
    return 0;
}
posted @ 2020-02-14 23:14  Sangber  阅读(180)  评论(0编辑  收藏  举报