简要题意:
给你一个n * n的非负矩阵,求问是否有子矩阵满足和在[k, 2k]之间。若有输出方案。n<=2000。
解:
首先n4暴力很好想(废话),然后发现可以优化成n3log2n,但是还是过不了.....
正解十分之玄妙.....
首先所有大于2k的都不可用。
然后若有一个子矩阵的和不小于k,那么一定有解,且解是这个矩阵的一个子矩阵。
证:
当这个矩阵的和大于2k时,切这个矩阵。
一定有一块大于k。
然后就这样了......
具体实现就看代码了。
1 #include <cstdio> 2 #include <algorithm> 3 4 typedef long long LL; 5 const int N = 2010; 6 const LL INF = (1ll << 63) - 1; 7 8 LL G[N][N], sum[N][N]; 9 int h[N][N], l[N], r[N], p[N], top; 10 11 inline LL getsum(int left, int right, int u, int d) { 12 return sum[d][right] - sum[d][left - 1] - sum[u - 1][right] + sum[u - 1][left - 1]; 13 } 14 15 int main() { 16 17 int n, m; 18 LL k; 19 scanf("%lld%d", &k, &n); 20 m = n; 21 for(int i = 1; i <= n; i++) { 22 for(int j = 1; j <= m; j++) { 23 scanf("%lld", &G[i][j]); 24 sum[i][j] = sum[i][j - 1] + sum[i - 1][j] - sum[i - 1][j - 1] + G[i][j]; 25 } 26 } 27 28 for(int j = 1; j <= m; j++) { 29 for(int i = 1; i <= n; i++) { 30 if(G[i][j] > (k << 1)) { 31 h[i][j] = 0; 32 } 33 else { 34 h[i][j] = h[i - 1][j] + 1; 35 } 36 //printf("h %d %d %d ", i, j, h[i][j]); 37 } 38 } 39 40 LL large = -INF; 41 int u, d, left, right; 42 43 for(int i = 1; i <= n; i++) { // down 44 top = 0; 45 p[0] = 0; 46 for(int j = 1; j <= m; j++) { 47 while(top && h[i][p[top]] >= h[i][j]) { 48 top--; 49 } 50 l[j] = p[top] + 1; 51 p[++top] = j; 52 } 53 top = 0; 54 p[0] = m + 1; 55 for(int j = m; j >= 1; j--) { 56 while(top && h[i][p[top]] >= h[i][j]) { 57 top--; 58 } 59 r[j] = p[top] - 1; 60 p[++top] = j; 61 } 62 63 for(int j = 1; j <= m; j++) { 64 LL t = getsum(l[j], r[j], i - h[i][j] + 1, i); 65 if(t > large) { 66 large = t; 67 u = i - h[i][j] + 1; 68 d = i; 69 left = l[j]; 70 right = r[j]; 71 } 72 } 73 } 74 75 if(large < k) { 76 printf("NIE"); 77 return 0; 78 } 79 80 81 while(large > (k << 1)) { 82 if(u < d) { 83 int mid = (u + d) >> 1; 84 if(getsum(left, right, u, mid) >= k) { 85 d = mid; 86 } 87 else { 88 u = mid + 1; 89 } 90 } 91 else { 92 int mid = (left + right) >> 1; 93 if(getsum(left, mid, u, d) >= k) { 94 right = mid; 95 } 96 else { 97 left = mid + 1; 98 } 99 } 100 large = getsum(left, right, u, d); 101 } 102 103 printf("%d %d %d %d", left, u, right, d); 104 105 return 0; 106 }