题解 - [POI2008]KUP-Plot purchase

[ P O I 2008 ]   K U P P l o t p u r c h a s e \mathrm{ [POI2008]\ KUP-Plot purchase} 题解

题目意思

  • [POI2008]KUP-Plot purchase
  • 你随意选择一个子矩阵使得子矩阵里的权值和 v [ k , 2 k ] v∈[k,2*k]
  • 一个坑点:输出横纵坐标要反一下的

S o l \mathrm{ Sol }

  • 前置知识:单调栈
  • 因为我们只要随意输出一个子矩阵,那么如果存在 a i , j [ k , 2 k ] a_{i,j}∈[k,2*k] 直接输出即可。
  • 那么肯定没这么简单:我们要找到这个矩阵的最大子矩阵,因为答案一定在这个矩阵当中。如果这个最大子矩阵的权值和 [ k , 2 k ] ∈[k,2*k] 就结束了。如果不满足,我们只要一行一行地切下去。
  • 对于切下来的那一行我们再做类似处理:如果权值和 ( 2 k , ) ∈(2*k,∞) ,那么我们一列一列地去切这一行直到 [ k , 2 k ] ∈[k,2*k] 。否则如果 [ k , 2 k ] ∈[k,2*k] 直接输出即可。再否则直接舍弃这行。

C o d e \mathrm{ 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;
			//情况1
		}
	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;
				//∈[m,2*m]已经特判了,此时只有<m才会产生贡献
		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;
}
			
	

猜你喜欢

转载自blog.csdn.net/wangyiyang2/article/details/105219299