bzoj4850 [JSOI2016]灯塔

\(\verb|bzoj4850 [JSOI2016]灯塔|\)

给定一个序列 \(a_i\) ,对于每个 \(i\) ,求出一个 \(p\) 使得对于任意 \(j\) ,都有 \(a_j\leq a_i+p-\sqrt {|i-j|}\)

\(n\leq10^5\)

数论分块


先将原式转化成与 \(p\) 相关的,即 \(p\ge a_j-a_i+\sqrt {|i-j|}\)

根号内上取整,答案不变,即 \(p=\max\{a_j-a_i\lceil\sqrt{|i-j|}\rceil\}\)

可以对根号取值数论分块,每段的贡献即求区间内 \(a_j\) 最大值,用 \(st\) 表 维护即可。

分块区间即为 \(\verb|当前位置|\pm\verb|根号取值|^2\)

时间复杂度 \(O(n\sqrt n\log n)\)

代码

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1e5 + 10;
int n, lg[maxn], st[18][maxn];

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

int main() {
  scanf("%d", &n);
  for (int i = 1; i <= n; i++) {
    scanf("%d", &st[0][i]);
  }
  for (int i = 2; i <= n; i++) {
    lg[i] = lg[i >> 1] + 1;
  }
  for (int i = 1; i <= lg[n]; i++) {
    for (int j = 1; j + (1 << i) - 1 <= n; j++) {
      st[i][j] = max(st[i - 1][j], st[i - 1][j + (1 << (i - 1))]);
    }
  }
  for (int i = 1; i <= n; i++) {
    int ans = 0;
    for (int k = 1, l = i + 1, r; l <= n; k++, l = r + 1) {
      r = min(n, i + k * k);
      ans = max(ans, query(l, r) - st[0][i] + k);
    }
    for (int k = 1, r = i - 1, l; r >= 1; k++, r = l - 1) {
      l = max(1, i - k * k);
      ans = max(ans, query(l, r) - st[0][i] + k);
    }
    printf("%d\n", ans);
  }
  return 0;
}

加强版 \(\verb|bzoj2216 [POI2011]Lightning Conductor|\) 留坑待填

猜你喜欢

转载自www.cnblogs.com/Juanzhang/p/10625045.html