TIOJ-1555

給一個$n\times m$的01矩陣,問有幾個正方形子矩陣滿足該子矩陣內的數字全都是1,並且輸出最長的邊長。

$n, m\le 5000$


這題有個十分重要的轉換手法,對於每一列我們維護一個長度為m的陣列a,a[i]代表第i行往上有幾個連續的0。
這個手段在TIOJ-1063也有用到。
接下來對每個(i,j)去計算以(i,j)為右下角可用出的正方形最大邊長(可以發現任何它短的邊長的正方形也都能放)。
如果要用長度為$x$的正方形,那必須滿足
$$\min_{k=j-x+1}^{j}(a[k])\ge x$$
由於隨著x變大,min只會越來越小,因此可以二分搜x,做到$\mathcal{O}(nmlogm)$。

如果我們改成考慮可以放出最大正方形的左界$l(l=j-x+1)$,然後就會發現隨著$j$增加,$l$只增不減(因為$j$增加也會使得min越來越小)。

所以可以使用雙指標,並且維護一個遞減的單調隊列來做RMQ(因為查詢的範圍只會越來越右邊)。
這樣就能做到$\mathcal{O}(nm)$。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
/*
-------------- | /
| | /
| | /
| * |/ | | ------ *
| | | | / \
| | |\ | | | |\ |
\ | | | \ | | | | \ |
\ | | | \ | | \ / \ |
V | | \ \__/| ----- \ |
*/
#include <bits/stdc++.h>
using namespace std;

#define EmiliaMyWife ios::sync_with_stdio(0); cin.tie(NULL);
using ll = int64_t;
using ull = uint64_t;
using ld = long double;
using uint = uint32_t;
const double EPS = 1e-8;
const int INF = 0x3F3F3F3F;
const ll LINF = 4611686018427387903;
const int MOD = 1e9+7;
/*--------------------------------------------------------------------------------------*/

signed main() { EmiliaMyWife
int n, m;
cin >> n >> m;
vector<string> arr(n);
for(int i = 0; i < n; i++)
cin >> arr[i];
vector<int> owo(m);
deque<int> line;

ll ans = 0, mx = 0;
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
if(arr[i][j] & 1)
owo[j] = 0;
else
owo[j]++;
}

for(int j = 0, it = 0; j < m; j++) {
while(!line.empty() && owo[line.back()] > owo[j])
line.pop_back();
line.push_back(j);
while(!line.empty() && owo[line.front()] < j - it + 1) {
it++;
if(line.front() < it)
line.pop_front();
}
ans += j - it + 1;
mx = max<ll>(mx, j - it + 1);
}
line.clear();
}
cout << ans << ' ' << mx << '\n';
}