[bzoj1127][悬线法]KUP

123 篇文章 1 订阅

Description

给一个n*n的地图,每个格子有一个价格,找一个矩形区域,使其价格总和位于[k,2k]

Input

输入k n(n<2000)和一个n*n的地图

Output

输出矩形的左上和右下的列-行坐标或NIE

Sample Input

inputdata1

4 3

1 1 1

1 9 1

1 1 1

inputdata2

8 4

1 2 1 3

25 1 2 1

4 20 3 3

3 30 12 2

Sample Output

outputdata1

NIE

outputdata2

2 1 4 2

HINT

1<=k<=10^9 每个价格都是不大于2*10^9的非负整数

题解

有点意思
1*1的直接判了
单考虑1*n的矩阵,如果有至少一个区间满足和>K且每个数都<=K那么一定合法
因为每个数都<=K,所以前缀和不会从<K直接蹦到>2*K
扩展到2维,同样成立
先悬线法求出每个数都<=K的和最大矩阵
考虑怎么从这里构造,选出最上面一行和最下面一行
设当前总和是S
显然至少有一行的和是<S/2的
由于原来的和>2*K
所以删去较小的那行后不会蹦到<K
那直接做就行了…

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
#include<ctime>
#include<map>
#include<bitset>
#define LL long long
#define mp(x,y) make_pair(x,y)
using namespace std;
inline int read()
{
	int f=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int stack[20];
inline void write(int x)
{
    if(!x){putchar('0');return;}
    int top=0;
    while(x)stack[++top]=x%10,x/=10;
    while(top)putchar(stack[top--]+'0');
}
inline void pr1(int x){write(x);putchar(' ');}
inline void pr2(int x){write(x);putchar('\n');}
LL prefix[2005][2005];
int n,K,a[2005][2005];
LL S(int u1,int v1,int u2,int v2){return prefix[u2][v2]-prefix[u1-1][v2]-prefix[u2][v1-1]+prefix[u1-1][v1-1];}
int L[2005][2005],R[2005][2005],up[2005][2005];
int ll[2005][2005],rr[2005][2005];
bool check(int nn1,int nn2)
{
	int u1=nn1-up[nn1][nn2]+1,v1=nn2-ll[nn1][nn2]+1;
	int u2=nn1,v2=nn2+rr[nn1][nn2]-1;
	if(S(u1,v1,u2,v2)<K)return false;
	while(S(u1,v1,u2,v2)>2*K)
	{
		if(u1<u2)
		{
			LL s1=S(u1,v1,u1,v2),s2=S(u2,v1,u2,v2);
			if(s1<s2)u1++;
			else u2--;
		}
		else
		{
			LL s1=S(u1,v1,u1,v1),s2=S(u2,v2,u2,v2); 
			if(s1<s2)v1++;
			else v2--;
		}
	}
	pr1(v1);pr1(u1);pr1(v2);pr2(u2);
	return true;
}
int main()
{
//	freopen("23.in","r",stdin);
//	freopen("a.out","w",stdout);
	K=read();n=read();
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)
	{
		a[i][j]=prefix[i][j]=read();
		if(a[i][j]>=K&&a[i][j]<=2*K)
		{
			pr1(j);pr1(i);pr1(j);pr2(i);
			return 0;
		}
	}
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)prefix[i][j]+=prefix[i][j-1];
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)prefix[i][j]+=prefix[i-1][j];
	
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(a[i][j]<K)L[i][j]=L[i][j-1]+1;
	for(int i=1;i<=n;i++)for(int j=n;j>=1;j--)if(a[i][j]<K)R[i][j]=R[i][j+1]+1;
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)
		if(a[i][j]<K)
		{
			if(i==1 ||a[i-1][j]>2*K)ll[i][j]=L[i][j],rr[i][j]=R[i][j],up[i][j]=1;
			else
			{
				ll[i][j]=min(ll[i-1][j],L[i][j]);rr[i][j]=min(rr[i-1][j],R[i][j]);
				up[i][j]=up[i-1][j]+1;
			}
		}
	
	LL mx=0;
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)mx=max(mx,S(i-up[i][j]+1,j-ll[i][j]+1,i,j+rr[i][j]-1));
	
	bool tf=false;
	for(int i=1;i<=n;i++)for(int j=1;j<=n;j++)if(S(i-up[i][j]+1,j-ll[i][j]+1,i,j+rr[i][j]-1)==mx)
	{
		tf|=check(i,j);
		if(tf)break;
	}
	if(!tf)puts("NIE");
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值