因为题目中要求使连续死亡的机器人最多,令人联想到二分答案。
考虑如何检验这之中是否存在一段连续的长度为md的区间,其中花最多k步使得它们都死亡。
这个条件等价于区间中m个最大值的和不超过k。
枚举起点,可以用 $ O(mlogn) $ 的时间确定这段区间是否合法,最终check的复杂度是 $ O(nmlogn) $。
总复杂度是 $ O(nmlog^{2}n) $。
$ igodot $ 技巧&套路:
- 最大(小)值的问题,可以考虑二分答案。
- check时用线段树优化区间平移,来枚举每一个长度固定的区间。
1 #include <cstdio> 2 #include <iostream> 3 #include <algorithm> 4 5 const int N = 100005; 6 7 int n, m, k, re; 8 int a[N][6], ans[6], tmp[6]; 9 10 namespace SE { 11 int ma[6][N << 2]; 12 inline void Up(int t) { 13 for (int i = 1; i <= m; ++i) { 14 ma[i][t] = std::max(ma[i][t << 1], ma[i][t << 1 | 1]); 15 } 16 } 17 void Build(int t, int l, int r) { 18 if (l == r) { 19 for (int i = 1; i <= m; ++i) ma[i][t] = a[l][i]; 20 return; 21 } 22 int md = (l + r) >> 1; 23 Build(t << 1, l, md); 24 Build(t << 1 | 1, md + 1, r); 25 Up(t); 26 } 27 int Query(int t, int l, int r, int L, int R, int ty) { 28 if (L <= l && r <= R) return ma[ty][t]; 29 int md = (l + r) >> 1, re = 0; 30 if (L <= md) re = std::max(re, Query(t << 1, l, md, L, R, ty)); 31 if (md < R) re = std::max(re, Query(t << 1 | 1, md + 1, r, L, R, ty)); 32 return re; 33 } 34 } 35 36 inline int Check(int md) { 37 for (int i = 1; i + md - 1 <= n; ++i) { 38 int sum = 0; 39 for (int j = 1; j <= m; ++j) { 40 tmp[j] = SE::Query(1, 1, n, i, i + md - 1, j); 41 sum += tmp[j]; 42 if (sum > k) break; 43 } 44 if (sum <= k) { 45 for (int j = 1; j <= m; ++j) ans[j] = tmp[j]; 46 return 1; 47 } 48 } 49 return 0; 50 } 51 52 int main() { 53 scanf("%d%d%d", &n, &m, &k); 54 for (int i = 1; i <= n; ++i) { 55 for (int j = 1; j <= m; ++j) { 56 scanf("%d", &a[i][j]); 57 } 58 } 59 SE::Build(1, 1, n); 60 for (int nl = 1, nr = n, md; nl <= nr; ) { 61 md = (nl + nr) >> 1; 62 if (Check(md)) { 63 re = md; nl = md + 1; 64 } else { 65 nr = md - 1; 66 } 67 } 68 if (re) Check(re); 69 for (int i = 1; i <= m; ++i) { 70 printf("%d ", (re)? ans[i] : 0); 71 } 72 73 return 0; 74 }