题解:P6600 「EZEC-2」字母
pengziyippp · · 题解
题目大意
给定一个
思路解析
观察题目条件,我们不难发现,找到一个T的关键是找到T的横竖的交叉点,再由此我们可以联想到使用前缀后缀预处理统计出交叉点向左,向右,向下最多能有多少个连续的
对于一个矩阵,我们可以暴力遍历每一个点,并且将其视为T的交叉点,根据前面统计出的前缀后缀快速得出在此交叉点上最大可以存在的T的横长和竖长。
但是,每个竖长横长还需满足题目所给出的如下条件:
-
w \ge a -
h \ge b -
w \times h \ge s -
w + h \ge x -
因此,我们又可以暴力预处理出在各个情况下有多少个T。
考虑用
由此可得:
for (int i = 3; i <= m; i ++ ) {
for (int j = 2; j <= n; j ++ ) {
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1];
if (i % 2 == 1 && i >= a && j >= b && (i * j) >= s && (i + j) >= x) {
sum[i][j] ++;
}
}
}
最后,我们只需要统计每一个交叉点存在的T的个数的总和即可得出最终答案。
完结撒花。
代码实现
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 3e3 + 10;
int vis[N][N], sum[N][N];
int ql[N][N], qr[N][N], qs[N][N];
signed main(){
ios::sync_with_stdio(false);
cin.tie(0); cout.tie(0);
int n, m, a, b, s, x;
cin >> n >> m >> a >> b >> s >> x;
string ts;
for (int i = 1; i <= n; i ++ ) {
cin >> ts;
int len = ts.size(); ts = " " + ts;
for (int j = 1; j <= len; j ++ ) {
if (ts[j] == '1') vis[i][j] = 1;
}
}
for (int i = 3; i <= m; i ++ ) {
for (int j = 2; j <= n; j ++ ) {
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1];
if (i % 2 == 1 && i >= a && j >= b && (i * j) >= s && (i + j) >= x) {
sum[i][j] ++;
}
}
} // 统计当横长小于等于i,竖长小于等于j时有几个符合条件的T
for (int i = 1; i <= n; i ++ ) {
for (int j = 1; j <= m; j ++ ) {
ql[i][j] = ql[i][j - 1] * vis[i][j] + vis[i][j];
}
} // 前缀统计一个点前面有多少个1
for (int i = 1; i <= n; i ++ ) {
for (int j = m; j >= 1; j -- ) {
qr[i][j] = qr[i][j + 1] * vis[i][j] + vis[i][j];
}
} // 后缀统计一个点后面有多少个1
for (int i = 1; i <= m; i ++ ) {
for (int j = n; j >= 1; j -- ) {
qs[j][i] = qs[j + 1][i] * vis[j][i] + vis[j][i];
}
} // 后缀统计一个点后面有多少个1
int ans = 0;
for (int i = 1; i <= n; i ++ ) {
for (int j = 1; j <= m; j ++ ) { // 暴力枚举每个交叉点
int minh = max (min (ql[i][j], qr[i][j]) * 2 - 1, (long long)(0)); // 横长
ans += sum[minh][qs[i][j]];
}
}
cout << ans;
}