[POI2008]KUP-Plot purchase(单调队列)
题意
给定k,n,和n*n的矩阵,求一个子矩形满足权值和在[k,2k]之间
,
题解
这里用到了极大化矩阵的思想。推荐论文《浅谈用极大化思想解决最大子矩阵问题》Orz
如果有一个元素在[k,2k]之间。直接输出就好。
否则。把所有大于2k的元素作为障碍点。
求每一个最大化矩阵。(用单调队列)
如果这个矩阵权值和大于等于k
那么这个矩阵一定有一个子矩阵满足条件。这个结论可以证明。
假设这个矩阵权值和小于等于2k则直接输出这个矩阵。
否则这个矩阵权值和一定大于2k
假设这个矩阵去掉第一行后权值和大于k,则去掉第一行后的矩阵继续操作。
假设权值和小于等于k则矩阵的第一行权值和一定大于k,所以一个一个去除这一行的元素判断即可。
1 #include<iostream> 2 #include<cstring> 3 #include<cstdio> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 const long long N=2010; 8 long long n,k,a[N][N],book[N][N],sum[N][N],sum1[N][N],top,stack[N],r[N],l[N]; 9 void work(long long x1,long long y1,long long x2,long long y2){ 10 if(sum1[x2][y2]-sum1[x2][y1-1]-sum1[x1-1][y2]+sum[x1-1][y1-1]<=2*k){ 11 cout<<y1<<" "<<x1<<" "<<y2<<" "<<x2; 12 return ; 13 } 14 while(sum1[x2-1][y2]-sum1[x2-1][y1-1]-sum1[x1-1][y2]+sum1[x1-1][y1-1]>=k){ 15 x2--; 16 } 17 while(sum1[x2][y2]-sum1[x2][y1-1]-sum1[x2-1][y2]+sum1[x2-1][y1-1]>2*k)y1++; 18 cout<<y1<<" "<<x2<<" "<<y2<<" "<<x2; 19 } 20 int main(){ 21 scanf("%lld%lld",&k,&n); 22 for(long long i=1;i<=n;i++) 23 for(long long j=1;j<=n;j++){ 24 scanf("%lld",&a[i][j]); 25 if(a[i][j]>=k&&a[i][j]<=2*k){ 26 cout<<j<<" "<<i<<" "<<j<<" "<<i; 27 return 0; 28 } 29 if(a[i][j]>2*k)book[i][j]=1; 30 if(book[i][j])sum[i][j]=0; 31 else sum[i][j]=sum[i-1][j]+1; 32 sum1[i][j]=sum1[i-1][j]+sum1[i][j-1]-sum1[i-1][j-1]+a[i][j]; 33 } 34 for(long long i=1;i<=n;i++){ 35 top=0; 36 for(long long j=1;j<=n;j++){ 37 while(sum[i][j]<sum[i][stack[top]]){ 38 r[stack[top]]=j-1; 39 top--; 40 } 41 stack[++top]=j; 42 } 43 while(top){ 44 r[stack[top--]]=n; 45 } 46 top=0; 47 for(long long j=n;j>=1;j--){ 48 while(sum[i][j]<sum[i][stack[top]]){ 49 l[stack[top]]=j+1; 50 top--; 51 } 52 stack[++top]=j; 53 } 54 while(top){ 55 l[stack[top--]]=1; 56 } 57 for(long long j=1;j<=n;j++){ 58 if(sum1[i][r[j]]-sum1[i][l[j]-1]-sum1[i-sum[i][j]][r[j]]+sum1[i-sum[i][j]][l[j]-1]>=k){ 59 work(i-sum[i][j]+1,l[j],i,r[j]); 60 return 0; 61 } 62 } 63 } 64 printf("NIE"); 65 return 0; 66 }