2 条题解
-
1
没有任何算法的一道,ac 秘诀在于步骤要清晰,尽量简明思路。
聪明的小朋友肯定想到了用行内前缀和,来判断该行区间是否全部能放人。
还有通过每列的前缀最大值,来判断当前格子能不能放这个高度的人。
同时可以给当前行选择区间的前缀最大值排序,和已经排序的 h 数组一个个相应下标匹配,但凡一个出问题,直接否定整个区间。
好了,打出来却发现 WA 和 TLE 找上门。
考虑优化,假设列区间已经固定,如果 i 行这个区间可以放人,那么 i 后面的行这个区间只要空的都可以放人。
即判断能否放人的列区间具有单调性。
直接二分可以放人的行边界,这个时候先只关注前缀最大值,最后累计答案时再关注空不空。
复杂度因为要二分和排序,是枚举 * 双 log,也就是 。
#include<bits/stdc++.h> using namespace std; typedef long long LL; const int N = 2010; LL a[N][N]; LL sum[N][N]; LL mx[N][N]; LL h[N]; LL now[N]; int n, m, K; bool check(int x, int y) { for (int j = y; j <= y + K - 1; j ++) { now[j - y + 1] = mx[x][j]; } sort(now + 1, now + K + 1); for (int i = 1; i <= K; i ++) { if (h[i] <= now[i]) { return 0; } } return 1; } int main () { ios::sync_with_stdio(false); cin.tie(0); cin >> n >> m >> K; for (int i = 1; i <= K; i ++) { cin >> h[i]; } sort (h + 1, h + K + 1); for (int i = 1; i <= n; i ++) { for (int j = 1; j <= m; j ++) { cin >> a[i][j]; } } for (int i = 1; i <= n; i ++) { sum[i][0] = 0; for (int j = 1; j <= m; j ++) { sum[i][j] = sum[i][j - 1] + a[i][j]; } } for (int j = 1; j <= m; j ++) { mx[0][j] = 0; for (int i = 1; i <= n; i ++) { mx[i][j] = max(mx[i - 1][j], a[i][j]); } } int ans = 0; for (int j = 1; j + K - 1 <= m; j ++) { int l = 1, r = n, p = 1; int mid; while (l <= r) { mid = (l + r) >> 1; if (check(mid, j)) { l = mid + 1; p = mid; } else { r = mid - 1; } } for (int i = 1; i <= p; i ++) { if (sum[i][j + K - 1] - sum[i][j - 1] == 0) { ans ++; } } } cout << ans << "\n"; return 0; }
信息
- ID
- 12643
- 时间
- 1500ms
- 内存
- 512MiB
- 难度
- 7
- 标签
- 递交数
- 44
- 已通过
- 11
- 上传者