洛谷3474 [POI2008]KUP-Plot purchase(悬线法)

题意

n*n的矩阵,求一个子矩形满足权值和在[k,2k]之间。

题解

悬线法
观察一下哪些地方可能产生权值在[k,2k]之间的矩阵?显然那种1*1的可以直接判断。一旦一个矩阵中出现了一个权值大于k的点,那么这个矩阵就作废了。
联系上奶牛浴场(洛谷1578)的道悬线法模版题,这些权值大于k的点就是坏点,任何一个矩阵都要避开这种点。(有点像墙)
回到题目上,我们还可以发现,因为我们是用权值小于k的点来拼凑矩阵的,所以如果找到了一个矩阵和大于2k的矩阵,那么这个矩阵一点包含一个合法子矩阵。
给出一种构造方法,对于一个权值和大于2k的矩阵,从上往下一层一层地削掉,每一层的权值一定是小于k的,(否则在那一层中,因为又满足小于等于2k,会被直接选为合法矩阵输出),那么一定有一个时候这个矩阵的权值会被削到[k,2k]之间。此时输出该矩阵。

代码

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=2010;

int n,k;
int a[maxn][maxn];ll sum[maxn][maxn];

int up[maxn][maxn];
int top=0,sta[maxn],stp[maxn];

ll S(int i1,int j1,int i2,int j2)
{
	return sum[i2][j2]-sum[i2][j1-1]-sum[i1-1][j2]+sum[i1-1][j1-1];
}

int main()
{
	scanf("%d%d",&k,&n);
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
		{
			scanf("%d",&a[i][j]);
			sum[i][j]=sum[i][j-1]+a[i][j];
		}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++) sum[i][j]+=sum[i-1][j];
	
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n+1;j++)
		{
			if(a[i][j]<=2*k && j<=n)//debug
			{
				up[i][j]=up[i-1][j]+1;
				if(a[i][j]>=k)
				{
					printf("%d %d %d %d\n",j,i,j,i);
					return 0;
				}
			}
			else up[i][j]=0;
			
			int far=j;
			while(top>0 && sta[top]>=up[i][j])
			{
				int x=i-sta[top]+1,y=stp[top];top--;
				far=min(far,y);
				if(k<=S(x,y,i,j-1) && S(x,y,i,j-1)<=2*k)//如果当前矩阵合法直接输出 
				{
					printf("%d %d %d %d\n",y,x,j-1,i);
					return 0;
				}
				else if(S(x,y,i,j-1)>2*k)//如果当前矩阵权值和大于2k,不断削顶 
					for(int k=x;;k++)//debug k--
						if(S(k,y,i,j-1)<=2*k)
						{
							printf("%d %d %d %d\n",y,k,j-1,i);
							return 0;
						}
			}
			if(j<=n) sta[++top]=up[i][j],stp[top]=far;
		}
	puts("NIE");
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值