bzoj1941 [Sdoi2010]Hide and Seek 线段树

这个题其实应该算4个二维偏序,因为枚举每个点可以根据公式分成4个象限,4种计算方式,所以就考虑怎么求x和y的大小关系集合

首先,按x排序再枚举,可以保证后面的x大于前面的x,就相当于分成了两部分,剩下的就是区分y的大小关系,相当于是在两个x的区间里找一个值左侧和右侧的信息,

如果分成4个信息,就相当于维护四个值:x+y,x-y,y-x,-x-y的最大最小值,   对于查询,就是标准的线段树问题,

所以动态维护线段树,在四个面里取最值就可以了


码:

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
#define zuo o<<1,l,mid
#define you o<<1|1,mid+1,r
#define N 100005
int xyd[N<<2],xyx[N<<2],fxyd[N<<2],fxyx[N<<2],fxfyd[N<<2],fxfyx[N<<2],xfyx[N<<2],xfyd[N<<2],a,b,maxx,minn,ans=999999999,op,i,n,dui[N];
struct la
{
	int x,y,id;	
}d[N],D[N];
bool cmp(la a,la b)
{
	if(a.x==b.x)
	 return a.y<b.y;
	 else return a.x<b.x;
}  
bool cmp2(la a,la b)
{
	if(a.y==b.y)
	 return a.x<b.x;
	 else return a.y<b.y;
}  
void up(int o)
{
	int ll=o<<1,rr=o<<1|1;
xyd[o]=max(xyd[ll],xyd[rr]);
fxyd[o]=max(fxyd[ll],fxyd[rr]);
xfyd[o]=max(xfyd[ll],xfyd[rr]);
fxfyd[o]=max(fxfyd[ll],fxfyd[rr]);
xyx[o]=min(xyx[ll],xyx[rr]);
fxyx[o]=min(fxyx[ll],fxyx[rr]);
xfyx[o]=min(xfyx[ll],xfyx[rr]);
fxfyx[o]=min(fxfyx[ll],fxfyx[rr]);
}
void jian(int o,int l,int r)
{
if(l==r)
{
	xfyx[o]=fxfyx[o]=fxyx[o]=999999999;
	xfyd[o]=fxfyd[o]=fxyd[o]=-999999999;
	xyx[o]=xyd[o]=D[l].x+D[l].y;
	fxyx[o]=fxyd[o]=D[l].y-D[l].x;	
	return ;
}
	int mid=(l+r)>>1;
	jian(zuo);
	jian(you);	
	up(o);
}
void gai(int o,int l,int  r)
{
	if(a<=l&&r<=b)
	{
		if(op==0)
		{
	xyx[o]=fxyx[o]=999999999;
	xyd[o]=fxyd[o]=-999999999;
		} 
	    if(op==1)
		{
	xfyx[o]=xfyd[o]=D[l].x-D[l].y;
	fxfyx[o]=fxfyd[o]=-D[l].y-D[l].x;	    	      	
		}
		if(op==2)
		{
	minn=min(minn,d[i].x-d[i].y-xfyd[o]);	
	maxx=max(maxx,d[i].x-d[i].y-xfyx[o]);			
		}
		if(op==3)
		{
	minn=min(minn,d[i].x+d[i].y+fxfyx[o]);	
	maxx=max(maxx,d[i].x+d[i].y+fxfyd[o]);			
		}	
		if(op==4)
		{
	minn=min(minn,xyx[o]-(d[i].x+d[i].y));	
	maxx=max(maxx,xyd[o]-(d[i].x+d[i].y));			
		}		
		if(op==5)
		{
	minn=min(minn,d[i].y-d[i].x-fxyd[o]);	
	maxx=max(maxx,d[i].y-d[i].x-fxyx[o]);			
		}
	     return;
	}
	int mid=(l+r)>>1;
	if(a<=mid)gai(zuo);
	if(b>mid)gai(you);
	up(o);
}
int main()
{
	scanf("%d",&n);
	for(i=1;i<=n;i++)
	{
		scanf("%d%d",&d[i].x,&d[i].y);	
		d[i].id=i;
		D[i]=d[i];	
	}
	sort(d+1,d+1+n,cmp);
	sort(D+1,D+1+n,cmp2);
	for(i=1;i<=n;i++)
	{
		dui[D[i].id]=i;		
	}
	jian(1,1,n);
	for(i=1;i<=n;i++)
	{
	op=0;//清除右面的 
	a=b=dui[d[i].id];
	gai(1,1,n);
	minn=999999999;
	maxx=-999999999;
	//计算 
	op=2;a=dui[d[i].id]+1;b=n;  //找左上角 
	if(a<=b)gai(1,1,n);
	op=3;a=1;b=dui[d[i].id]-1;  //找左下角 
	if(a<=b)gai(1,1,n);	
	op=4;a=dui[d[i].id]+1;b=n;  //找右上角 
	if(a<=b)gai(1,1,n);		
	op=5;a=1;b=dui[d[i].id]-1;  //找右下角 
	if(a<=b)gai(1,1,n);		
		ans=min(maxx-minn,ans);			
	op=1;//加入左面的 
a=b=dui[d[i].id];
	gai(1,1,n);
	}
	printf("%d",ans);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值