BZOJ1127: [POI2008]KUP

来源:互联网 发布:淘宝商品品牌怎么填写 编辑:程序博客网 时间:2024/05/19 20:20
题目大意:给一个n*n的非负整数矩阵和一个常数K,要求你找出一个子矩阵使得这个子矩阵的数字和在[K,2K]之间

首先假设有一个格子就在这个范围内,直接输出
否则格子分两种,一种小于K,一种大于2K,其中第二种格子不能选
然后一个比较显然的结论就是,我们若能找出一种不包含第二个格子的子矩形使得其权值和大于等于K,这个子矩形一定有一部分的权值和小于等于2K,因为里面每个元素都不超过K

所以我们可以用一个单调栈搞出所有的极大子矩形,然后若其权值大于2K,就开始砍行和列,直到小于等于2K为止

#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#define N 2010using namespace std;int a[N];bool mp[N][N];long long pre[N][N];void YES(int A,int B,int C,int D){printf("%d %d %d %d",A,B,C,D);exit(0);}int s[N],t;long long cal(int A,int B,int C,int D){return pre[C][D]-pre[A-1][D]-pre[C][B-1]+pre[A-1][B-1];}int K,n;void cut(int A,int B,int C,int D){while(cal(A,B,C,D)>2*K){if(A==C) D--;else if(cal(A+1,B,C,D)>=K) A++;else C--;}YES(B,A,D,C);}int main(){scanf("%d%d",&K,&n);int i,j,x,y;for(i=1;i<=n;i++)for(j=1;j<=n;j++){scanf("%d",&x);if(x>=K&&x<=2*K) YES(j,i,j,i);pre[i][j]=pre[i-1][j]+pre[i][j-1]-pre[i-1][j-1]+x;mp[i][j]=(x<K);}for(j=1;j<=n;j++){for(i=1;i<=n;i++)a[i]=mp[j][i]?a[i]+1:0;for(i=1;i<=n;i++){while(t&&a[s[t]]>=a[i]){if(cal(j-a[s[t]]+1,s[t-1]+1,j,i-1)>=K) cut(j-a[s[t]]+1,s[t-1]+1,j,i-1);t--;}t++;s[t]=i;}while(t){if(cal(j-a[s[t]]+1,s[t-1]+1,j,n)>=K) cut(j-a[s[t]]+1,s[t-1]+1,j,n);t--;}}puts("NIE");}