题目
题意概要
有一个
n
×
m
n\times m
n×m 的方格图,其中有
p
p
p 个矩形建筑。求图中没有与矩形重叠的正方形中最大的一个。
数据范围与约定
n
,
m
≤
1
0
6
,
p
≤
4
×
1
0
4
n,m\le 10^6,p\le 4\times 10^4
n,m≤106,p≤4×104
思路
使用类似于 尺取法 的方法。或者叫做双扫描线吧。平行于 y y y 轴的扫描线。
对于左端点是 l l l 、右端点是 r r r 的情况,我们认为在这之间存在边长为 r − l + 1 r-l+1 r−l+1 的正方形。试图将 r r r 增大一,我们求一个最宽的上下界 u , d u,d u,d ,满足第 u u u 行、第 u + 1 u+1 u+1 行、第 u + 2 u+2 u+2 行,一直到第 d d d 行,都是没有建筑物的。
形如
如果这个空隙 u − d + 1 u-d+1 u−d+1 是大于等于 r − l r-l r−l 的,那么就可以将 r r r 增大 1 1 1 。
这个空隙的大小,可以使用线段树解决。解决一个 “最长全零子段” 的问题。
维护线段树,可以通过 “遇到右端点就区间减、左端点就区间加” 的方法。
然后没了。如果你对时间复杂度 O ( n log n ) \mathcal O(n\log n) O(nlogn) 不太放心,有几个优化:
- 对纵坐标进行离散化,线段树的值域降为 O ( p ) \mathcal O(p) O(p) 的。
- 对横坐标进行离散化(注意到合法的、最大的 r r r 总是在某个建筑的左端点旁边),尺取次数降为 O ( p ) \mathcal O(p) O(p) 。
两者同时使用,理论上来说,是
O
(
p
log
p
)
\mathcal O(p\log p)
O(plogp) 的。但是伪代码中没有使用;你也没必要做的这么绝。
代码
此处仅提供伪代码作为参考。因为我还没写出来
SegmentTree st;
void work(){
int l = 1, r = 0, ans = 0;
while(r != n and l <= n){
while(r < n){
for(左端点是第r+1列的矩形mat)
在线段树中将[up,down]增大1
if(st.query() >= r-l)
++ r; // query() 为最大全零子段
else 撤销本轮的add操作
}
ans = max(ans,r-l+1);
for(右端点是第l列的矩形mat)
在线段树中将[up,down]减少1
++ l; // 强制移动一下l
}
cout << ans << endl;
}