题解:ABC410F Balanced Rectangles
题意
给定一个 \(n\times m\) 的网格,每一格都是 . 或 #。求有多少非空子矩形,使得 . 和 # 个数相同。多测,\(1\leq T\leq 2.5\times 10^4\),\(1\leq nm\leq \sum{nm}\leq 3\times 10^5\)。
题解
还是在比赛结束前 \(10\) 分钟 rush 出来了。
把 . 视作 \(-1\),# 视作 \(1\),那问题就变为求有多少子矩形和为 \(0\)。
怎么做?二维感觉不好想,先考虑一维的情况。我们发现这个简单得要命,就是求 \(pre[0,n]\) 中有多少二元组 \((i,j)(i<j)\) 满足 \(s_i=s_j\),我们开一个 map 统计出现次数,一边计算 \(pre\) 一边累加答案即可。
回到二维的情况。我们不难得到一种暴力的做法:枚举子矩形的行上下边界 \(l,r\),然后令 \(b_i=\sum_{j=l}^ra_{i,j}\),我们发现这样问题就退化成一维的情况了,时间复杂度 \(\mathcal{O}(\sum{n^2m\log{m}})\)。
然后就是比较典的东西了。若 \(n\leq m\),我们就枚举上下边界,若 \(n>m\),我们就枚举左右边界,时间复杂度 \(\mathcal{O}(\sum{nm\min(n,m)\log{\max(n,m)}})\)。而 \(\min(n,m)\leq \sqrt{nm}\),所以这样子时间复杂度不超过 \(\mathcal{O}(\sum{nm}\sqrt{nm}\log{nm})\)。
然后某个唐诗就直接交了,吃了一发罚时。怎么会这么唐呢???
考虑优化。由于 \(a_{i,j}\in\{-1,1\}\),所以子矩形和的值域为 \([-nm,nm]\),把 map 换成桶即可,清空时直接撤销即可保证复杂度。
时间复杂度 \(\mathcal{O}(\sum{nm\min(n,m)})\)。
代码
#include <iostream>
#include <vector>
#include <string>
#include <map>
using namespace std;
#define lowbit(x) ((x) & -(x))
#define chk_min(x, v) (x) = min((x), (v))
#define chk_max(x, v) (x) = max((x), (v))
typedef long long ll;
typedef pair<int, int> pii;
const int N = 3e5 + 5, D = 3e5;
int t, n, m, cnt[N << 1];
ll ans;
string a[N];
vector<ll> s[N];
ll d[N];
int main() {
ios::sync_with_stdio(0), cin.tie(0);
cin >> t;
while (t--) {
cin >> n >> m;
for (int i = 1; i <= n; ++i) cin >> a[i];
s[0].resize(m + 1);
for (int i = 0; i <= m; ++i) s[0][i] = 0;
for (int i = 1; i <= n; ++i) {
s[i].resize(m + 1);
s[i][0] = 0;
for (int j = 1; j <= m; ++j) s[i][j] = (a[i][j - 1] == '.' ? 1 : -1);
}
ans = 0;
if (n <= m) {
for (int l = 1; l <= n; ++l) {
for (int i = 1; i <= m; ++i) d[i] = 0;
for (int r = l; r <= n; ++r) {
for (int i = 1; i <= m; ++i) d[i] += s[r][i];
cnt[D] = 1;
ll sum = 0;
for (int i = 1; i <= m; ++i) sum += d[i], ans += cnt[sum + D], ++cnt[sum + D];
sum = 0;
for (int i = 1; i <= m; ++i) sum += d[i], --cnt[sum + D];
}
}
} else {
for (int l = 1; l <= m; ++l) {
for (int i = 1; i <= n; ++i) d[i] = 0;
for (int r = l; r <= m; ++r) {
for (int i = 1; i <= n; ++i) d[i] += s[i][r];
cnt[D] = 1;
ll sum = 0;
for (int i = 1; i <= n; ++i) sum += d[i], ans += cnt[sum + D], ++cnt[sum + D];
sum = 0;
for (int i = 1; i <= n; ++i) sum += d[i], --cnt[sum + D];
}
}
}
cout << ans << '\n';
}
return 0;
}

浙公网安备 33010602011771号