BZOJ2702 金融风暴 [决策单调性]

金融风暴

先orz Claris

好像是很经典的做法。考虑一行左边两个点的答案更新右边的某个点。不难发现是有决策单调性的。右边更新左边同理。那么把每个点的初始距离设置为上方/下方最近的点,并对每一行分分治即可。至于查询用二维ST表就可以。

#include <bits/stdc++.h>

inline int rd() {
  int a = 1, b = 0; char c = getchar();
  while (!isdigit(c)) a = c == '-' ? 0 : 1, c = getchar();
  while (isdigit(c)) b = b * 10 + c - '0', c = getchar();
  return a ? b : -b;
}

const int N = 1005;

int w, h, n, q, map[N][N], st[11][N][N];
int up[N], f[N], lg[N];

inline void solve(int l, int r, int ql, int qr) {
  int mid = (l + r) >> 1, pos = 0;
  for (int i = ql; i <= qr; ++i) if (~up[i]) {
    int tmp = (mid - i) * (mid - i) + up[i] * up[i];
    if (f[mid] > tmp) {
      f[mid] = tmp;
      pos = i;
    }
  }
  if (pos && l != mid)
    solve(l, mid - 1, ql, pos);
  if (pos && r != mid)
    solve(mid + 1, r, pos, qr);
}

inline int query(int x, int y, int d) {
  int t = lg[d];
  return std::max({
    st[t][x][y], st[t][x + d - (1 << t)][y + d - (1 << t)],
    st[t][x + d - (1 << t)][y], st[t][x][y + d - (1 << t)]
  });
}

int main() {
  for (int i = 2; i < N; ++i)
    lg[i] = lg[i >> 1] + 1;
  w = rd() + 1, h = rd() + 1, n = rd();
  for (int i = 1; i <= n; ++i) {
    int x = rd() + 1, y = rd() + 1;
    map[x][y] = 1;
  }
  memset(up, -1, sizeof(up));
  for (int i = 1; i <= w; ++i) {
    memset(f, 0x3f, sizeof(f));
    for (int j = 1; j <= h; ++j) {
      if (map[i][j]) up[j] = 0;
      else if (~up[j]) ++up[j];
    }
    solve(1, h, 1, h);
    for (int j = 1; j <= h; ++j)
      st[0][i][j] = f[j];
  }
  memset(up, -1, sizeof(up));
  for (int i = w; i >= 1; --i) {
    memset(f, 0x3f, sizeof(f));
    for (int j = 1; j <= h; ++j) {
      if (map[i][j]) up[j] = 0;
      else if (~up[j]) ++up[j];
    }
    solve(1, h, 1, h);
    for (int j = 1; j <= h; ++j)
      st[0][i][j] = std::min(st[0][i][j], f[j]);
  }
  for (int k = 1; k <= 10; ++k)
    for (int i = 1; i + (1 << k) - 1 <= w; ++i)
      for (int j = 1; j + (1 << k) - 1 <= h; ++j)
        st[k][i][j] = std::max({
          st[k - 1][i][j], st[k - 1][i][j + (1 << (k - 1))],
          st[k - 1][i + (1 << (k - 1))][j],
          st[k - 1][i + (1 << (k - 1))][j + (1 << (k - 1))]
        });
  q = rd();
  while (q--) {
    int x = rd() + 1, y = rd() + 1, d = rd();
    if (x - d < 1 || x + d > w || y - d < 1 || y + d > h) {
      puts("-1"); continue;
    }
    int ans = query(x - d, y - d, d * 2 + 1);
    printf("%.3lf\n", std::sqrt(ans));
  }
  return 0;
}

发表评论

电子邮件地址不会被公开。必填项已用 * 标注