[POI2008] KUP-Plot purchase

发布时间 2023-08-15 19:10:42作者: -白简-

简明题意

对于给出的矩阵,在其中找到一个子矩阵使得子矩阵的和大于等于 \(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;
}