P11503 [NordicOI 2018] Nordic Camping

P11503 [NordicOI 2018] Nordic Camping

花了我挺长时间。

帐篷都是正方形的,可以枚举左上角,二分正方形边长,二维前缀和判断是否合法。这部分复杂度为 \(O(n^2\log n)\)。处理出来后,问题似乎就变成了矩形取最大值,单点查询。直接做是 \(\log^2\)65 pts。具体就是,先扫描线(扫描线竖向),我们类似标记永久化,但修改的标记我们记录到线段树中每个节点中(可以随便用 STL 维护,例如 set、优先队列懒惰删除),撤销的时候暴力删除。

然后就考虑需要发现一点性质,我们所谓的“矩形取最大值,单点查询”,矩形全是正方形,值是正方形的面积。

我们依旧扫描线。考虑上面几种情况,蓝色正方形没有任何影响不用考虑。橙色正方形和黑色正方形重叠的部分,答案肯定是黑色,这时候橙色正方形对后面就没有任何影响了。粉色正方形影响的范围比黑色远,它还是可能有用的。这其实就是单调队列的过程,把上面的 STL 替换为单调队列,复杂度被优化为 \(O(n^2\log n)\)

#include <bits/stdc++.h>

using namespace std;

#define IL inline
#define fi first
#define se second
using pii = pair<int, int>;
using ubt = long long;

IL void ckx(int &x, const int &y) { (x < y) && (x = y); }

const int N = 2000, M = 1e5;
const int maxN = N + 3, maxM = M + 3;

#define ls (p << 1)
#define rs (p << 1 | 1)
int NOW;
deque<pii> t[maxN << 2];
void C(int L, int R, int T, int v, int p, int l, int r) {
  if (L <= l && r <= R) {
    if (!t[p].empty() && t[p].back().se >= T) return; // 蓝色无用
    while (!t[p].empty() && t[p].back().fi <= v) t[p].pop_back();
    t[p].emplace_back(v, T);
    return;
  }
  int mid = (l + r) >> 1;
  if (L <= mid) C(L, R, T, v, ls, l, mid);
  if (R > mid) C(L, R, T, v, rs, mid + 1, r);
}
void ask(int K, int p, int l, int r, int &ans) {
  while (!t[p].empty() && t[p].front().se < NOW) t[p].pop_front();
  if (!t[p].empty()) ckx(ans, t[p].front().fi);
  if (l == r) return;
  int mid = (l + r) >> 1;
  if (K <= mid) ask(K, ls, l, mid, ans);
  else ask(K, rs, mid + 1, r, ans);
}

int n, m;
char c[maxN][maxN];
int s[maxN][maxN];
IL int get(int x, int y, int a, int b) {
  return s[a][b] - s[a][y - 1] - s[x - 1][b] + s[x - 1][y - 1];
}

int tot;
struct Modi {
  int y, l, r, v, T;
} mo[N * N + 3];

int Q;
struct ques {
  int x, y, id;
} q[maxM];
int ans[maxM];

int main() {
  scanf("%d %d", &n, &m);
  for (int i = 1; i <= n; ++i)
    for (int j = 1; j <= m; ++j) {
      do c[i][j] = getchar(); while (c[i][j] != '.' && c[i][j] != '#');
      s[i][j] = c[i][j] == '#';
    }
  for (int i = 1; i <= n; ++i)
    for (int j = 1; j <= m; ++j)
      s[i][j] += s[i - 1][j] + s[i][j - 1] - s[i - 1][j - 1];

  for (int x = 1; x <= n; ++x)
    for (int y = 1; y <= m; ++y) if (c[x][y] != '#') {
      int l = 1, r = min(n - x + 1, m - y + 1);
      while (l <= r) {
        int mid = (l + r) >> 1;
        if (!get(x, y, x + mid - 1, y + mid - 1))
          l = mid + 1;
        else r = mid - 1;
      }
      mo[++tot] = {y, x, x + r - 1, r, y + r - 1};
    }
  sort(mo + 1, mo + tot + 1, [](const auto &A, const auto &B) {
    return A.y < B.y;
  });
  
  scanf("%d", &Q);
  for (int i = 1; i <= Q; ++i)
    scanf("%d %d", &q[i].x, &q[i].y), q[i].id = i;
  sort(q + 1, q + Q + 1, [](const auto &A, const auto &B) {
    return A.y < B.y;
  });

  int now = 1;
  for (int i = 1; i <= Q; ++i) {
    NOW = q[i].y;
    while (now <= tot && mo[now].y <= q[i].y) {
      C(mo[now].l, mo[now].r, mo[now].T, mo[now].v, 1, 1, n);
      now++;
    }
    ask(q[i].x, 1, 1, n, ans[q[i].id]);
  }

  for (int i = 1; i <= Q; ++i)
    printf("%d\n", ans[i] * ans[i]);
}
posted @ 2025-01-04 07:30  ccxswl  阅读(48)  评论(0)    收藏  举报