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]);
}