[POI2008] KUP-Plot purchase
简明题意
对于给出的矩阵,在其中找到一个子矩阵使得子矩阵的和大于等于 \(k\) 且小于等于 \(2k\)。
思路
首先我们知道,如果一个数在 \(\left[ k,2k \right]\),这个数就是答案;如果一个数大于 \(2k\),那这个数不能出现在子矩阵中。
把这两种点排除出去,我们剩下的矩阵就只剩下了一些值小于 \(k\) 的点。
假设我们找到了一个极大子矩阵,矩阵和 \(sum \geq k\)。要是不存在这样一个极大子矩阵,就是无解的情况了。
如果 \(sum \leq 2k\) 就可以直接输出答案了,否则我们可以挑出该矩阵的第一行的元素和 \(sum_1\)。
如果 \(sum_1 \geq k\),那么答案就在第一行里,一个个元素删就可以了。因为每个元素的值都是小于 \(k\) 的,所以不可能有某个元素被删去后能够使得矩阵和从大于 \(2k\) 跳到小于 \(k\),就意味着我们只要一个个删去第一行中的元素,就一定可以找到答案。
如果 \(sum_1 < k\),我们知道 \(sum\) 是大于 \(k\) 的,我们就可以舍弃掉第一行,对剩下的矩阵重复上面的操作。
利用悬线法找到极大子矩阵。
Code
#include <algorithm>
#include <iostream>
#include <climits>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <ctime>
#include <cstdlib>
using namespace std;
const int N = 2050;
int n,k;
long long val[N][N];
long long sum[N][N];
int l[N][N],r[N][N],h[N][N];
long long Get(int a,int b,int c,int d) {
return sum[c][d] - sum[a - 1][d] - sum[c][b - 1] + sum[a - 1][b - 1];
}
bool flag = 0;
void Work(int a,int b,int c,int d) {
while(Get(a,b,c,d) > 2 * k) {
if(a == c)
d --;
else if(Get(a,b,c,d) >= k)
c = a;
else
a ++;
}
cout << b << " " << a << " " << d << " " << c;
exit(0);
}
signed main() {
// freopen("matrix.in","r",stdin);
// freopen("matrix.out","w",stdout);
scanf("%d%d",&k,&n);
for(int i = 1;i <= n; i++) {
for(int j = 1;j <= n; j++) {
cin >> val[i][j];
sum[i][j] = sum[i - 1][j] + sum[i][j - 1] + val[i][j] - sum[i - 1][j - 1];
if(val[i][j] < k || val[i][j] > 2 * k)
continue;
cout << j << " " << i << " " << j << " " << i << "\n";
return 0;
}
}
for(int i = 1;i <= n; i++)
r[0][i] = n + 1;
for(int i = 1;i <= n; i++)
for(int j = 1;j <= n; j++)
if(val[i][j] > 2 * k)
h[i][j] = 0;
else
h[i][j] = h[i - 1][j] + 1;
for(int i = 1;i <= n; i++) {
int tmp = 0;
for(int j = 1;j <= n; j++) {
if(h[i][j])
l[i][j] = max(l[i - 1][j],tmp);
else {
tmp = j;
l[i][j] = 0;
}
}
tmp = n + 1;
for(int j = n;j; j--) {
if(h[i][j])
r[i][j] = min(r[i - 1][j],tmp);
else {
tmp = j;
r[i][j] = n + 1;
}
}
}
int a,b,d;
for(int i = 1;i <= n; i++) {
for(int j = 1;j <= n; j++) {
if(h[i][j] == 0)
continue;
a = i - h[i][j] + 1;
b = l[i][j] + 1;
d = r[i][j] - 1;
if(Get(a,b,i,d) < k)
continue;
Work(a,b,i,d);
}
}
cout << "NIE";
return 0;
}