[POI2008] KUP−Plotpurchase 题解
题目意思
Sol
- 前置知识:单调栈
- 因为我们只要随意输出一个子矩阵,那么如果存在
ai,j∈[k,2∗k]直接输出即可。
- 那么肯定没这么简单:我们要找到这个矩阵的最大子矩阵,因为答案一定在这个矩阵当中。如果这个最大子矩阵的权值和
∈[k,2∗k]就结束了。如果不满足,我们只要一行一行地切下去。
- 对于切下来的那一行我们再做类似处理:如果权值和
∈(2∗k,∞),那么我们一列一列地去切这一行直到
∈[k,2∗k]。否则如果
∈[k,2∗k]直接输出即可。再否则直接舍弃这行。
Code
#include <bits/stdc++.h>
#define pb push_back
#define int long long
using namespace std;
inline int read()
{
int sum=0,ff=1; char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-') ff=-1;
ch=getchar();
}
while(isdigit(ch))
sum=sum*10+(ch^48),ch=getchar();
return sum*ff;
}
const int N=2005;
int n,m,a[N][N],b[N][N],ans;
int up[N][N],stak[N],L[N],R[N],top;
int ax,ay,bx,by;
inline int sum(int sx,int sy,int ex,int ey)
{
return b[ex][ey]-b[sx-1][ey]-b[ex][sy-1]+b[sx-1][sy-1];
}
signed main()
{
m=read();
n=read();
for ( int i=1;i<=n;i++ )
for ( int j=1;j<=n;j++ )
{
a[i][j]=read();
if(a[i][j]>=m&&a[i][j]<=m*2)
return printf("%lld %lld %lld %lld\n",j,i,j,i),0;
}
for ( int i=1;i<=n;i++ )
for ( int j=1;j<=n;j++ )
b[i][j]=b[i][j-1]+b[i-1][j]-b[i-1][j-1]+a[i][j];
int mx=0;
for ( int i=1;i<=n;i++ )
{
for ( int j=1;j<=n;j++ )
if(a[i][j]<m)
up[i][j]=up[i-1][j]+1;
stak[top=1]=0;
up[i][0]=-1;
for ( int j=1;j<=n;j++ )
{
while(top&&up[i][stak[top]]>=up[i][j]) top--;
L[j]=stak[top]+1;
stak[++top]=j;
}
stak[top=1]=n+1;
up[i][n+1]=-1;
for ( int j=n;j>=1;j-- )
{
while(top&&up[i][stak[top]]>=up[i][j]) top--;
R[j]=stak[top]-1;
stak[++top]=j;
if(up[i][j])
{
int now=sum(i-up[i][j]+1,L[j],i,R[j]);
if(now>=m&&now<=2*m)
return printf("%lld %lld %lld %lld\n",L[j],i-up[i][j]+1,R[j],i),0;
if(now>mx)
{
mx=now;
ax=i-up[i][j]+1,ay=L[j];
bx=i,by=R[j];
}
}
}
}
if(mx<m) return printf("NIE\n"),0;
for ( int i=bx;i>=ax;i-- )
{
int now=sum(i,ay,i,by);
if(now>=m&&now<=2*m)
return printf("%lld %lld %lld %lld\n",ay,i,by,i),0;
if(now>2*m)
{
mx=now;
for ( int j=by;j>=ay;j-- )
{
mx-=a[i][j];
if(mx>=m&&mx<=2*m)
return printf("%lld %lld %lld %lld\n",ay,i,j-1,i),0;
}
}
else
{
mx-=now;
if(mx>=m&&mx<=2*m)
return printf("%lld %lld %lld %lld\n",ay,ax,by,i-1),0;
}
}
return 0;
}