TIOJ-1669

給一個$n\times m$的二維矩陣,包含-1 0 +1,和一個$k$。

求一個最小的子矩陣滿足裡面的和至少為$k$。

$1\le n, m\le 500, 0\le k\le 2.5\times 10^5$


一個老套的子矩陣問題,首先枚舉一個維度的左右界,然後就能把那個左右界內的和都壓到剩下那維上,於是變成了一維的問題。
接著問題就變成給一個序列,問最小的subarray長度滿足和至少為$k$,如果序列都是正數就能用雙指針了,然而這題有負數。

怎麼辦呢? 先給他們做前綴和,然後枚舉右界$r$,之後就是找一個最大的 $l$ 滿足$sum_r-sum_l\ge k$,注意到如果有$l_1 < l_2$且$sum_{l_1}\ge sum_{l_2}$那$l_2$肯定優於$l_1$(因為可以用更小的區間長度湊出更大的區間和)。

這樣單調性就出來了! 維護一個單調遞增的stack,然後把上面式子整理一下得$sum_r-k\ge sum_l$,也就是要找出最不超過$sum_r-k$中最大的那個$sum_l$,就只要二分搜就好。

因為我是枚舉$n$那一維,所以我的複雜度是$\mathcal{O}(n^2mlogm)$。

關於實作的部分,因為根本不用存前綴和,所以我只用一個變數cur當前綴和用。

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
60
61
62
63
64
65
66
/*
-------------- | /
| | /
| | /
| * |/ | | ------ *
| | | | / \
| | |\ | | | |\ |
\ | | | \ | | | | \ |
\ | | | \ | | \ / \ |
V | | \ \__/| ----- \ |
*/
#include <bits/stdc++.h>
using namespace std;

#define EmiliaMyWife ios::sync_with_stdio(0); cin.tie(NULL);
const int INF = 0x3F3F3F3F;
/*--------------------------------------------------------------------------------------*/

inline char owo(char c) { return c == '+' ? 1 : (c == '.' ? 0 : -1); }

const int N = 500;
string arr[N];
int sum[N];

signed main() { EmiliaMyWife
int n, m, nd;
cin >> n >> m >> nd;

for(int i = 0; i < n; i++)
cin >> arr[i];

int ans = INF;

vector<pair<int, int>> line;
line.reserve(m);

for(int i = 0; i < n; i++) {
memset(sum, 0, sizeof sum);

for(int j = i; j < n; j++) {
for(int k = 0; k < m; k++)
sum[k] += owo(arr[j][k]);

int cur = 0;
line.clear();
line.push_back({0, -1});

for(int k = 0; k < m; k++) {
cur += sum[k];

auto it = upper_bound(line.begin(), line.end(), make_pair(cur - nd, INF));
if(it != line.begin())
ans = min(ans, (k - prev(it)->second) * (j - i + 1));

while(!line.empty() && line.back().first >= cur)
line.pop_back();
line.push_back({cur, k});
}
}
}

if(ans == INF)
cout << "-1\n";
else
cout << ans << '\n';
}