专栏文章

题解:P6600 「EZEC-2」字母

P6600题解参与者 1已保存评论 0

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
0 条
当前快照
1 份
快照标识符
@miouwmfv
此快照首次捕获于
2025/12/03 01:33
3 个月前
此快照最后确认于
2025/12/03 01:33
3 个月前
查看原文

题目大意

给定一个 n×mn\times m0101 矩阵, 要求你找出其中有多少个由 11 组成的并且符合条件的字母T。

思路解析

观察题目条件,我们不难发现,找到一个T的关键是找到T的横竖的交叉点,再由此我们可以联想到使用前缀后缀预处理统计出交叉点向左,向右,向下最多能有多少个连续的 11
对于一个矩阵,我们可以暴力遍历每一个点,并且将其视为T的交叉点,根据前面统计出的前缀后缀快速得出在此交叉点上最大可以存在的T的横长和竖长。
但是,每个竖长横长还需满足题目所给出的如下条件:
  • waw \ge a
  • hbh \ge b
  • w×hsw \times h \ge s
  • w+hxw + h \ge x
  • ww 为奇数
因此,我们又可以暴力预处理出在各个情况下有多少个T。
考虑用 sum[i][j]sum[i][j] 表示横长为 ii 竖长为 jj 时在条件下有多少个符合条件的T。
由此可得:
CPP
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的个数的总和即可得出最终答案。
完结撒花。

代码实现

CPP
#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;
}

评论

0 条评论,欢迎与作者交流。

正在加载评论...