brother[扫描线]

描述:
胜负胸中料已明,又从堂上出奇兵。秋实大哥是一个下棋好手,独孤求败的他觉
得下棋已经无法满足他了,他开始研究一种新的玩法。
在一个 n×m 的棋盘上,放置了 k 个车,并且他在棋盘上标出了 q 个矩形,表示
矩形内部是战略要地。
秋实大哥要求一个矩形内的每一个格子,都至少能被一辆在矩形内的车攻击到,
那么这个矩形就是被完整保护的。
现在秋实大哥想知道每一个矩形是否被完整保护。
输入:
第一行包含四个整数 n,m,k,q,表示棋盘的大小,车的数量以及矩形的数量。
接来下 k 行,每行包含两个整数 x,y,表示有一辆车位于从左往右第 x 列,从
下往上第 y 行。
接下来 q 行,每行包含四个整数 x1,y1,x2,y2,表示一个矩形的左下角和右
上角坐标。
1≤n,m≤1e5,1≤k,q≤2e5,1≤x1≤x2≤1e5,1≤y1≤y2≤1e5,1≤x≤1e5,
1≤y≤1e5。
输出:
输出 q 行, 对于每一次询问, 这个矩形若被完整保护了输出"YES", 否则输出"NO"。
输入输出样例:
brother.in brother.out
4 3 3 3
1 1
3 2
2 3
2 3 2 3
2 1 3 3

1 2 2 3


之前没接触过扫描线,实在是不会,等大神讲了后才懂~~~


------------------------------------------------------------------------------

我们将矩形按x2排个序,把车按x大小排个序,用线段树维护当前y这一列离x2最近的车的x坐标

要不画个图?(x2是当前扫描线,黑色方框代表车,很明显有个车在矩阵外,当前这一列没被覆盖,这个矩阵也没被覆盖)


然后枚举所有格矩阵,将xi<=x的车的x在线段树y的位置更新。

然后查询矩阵区间[y1,y2]在线段树中的最小值(最靠左的车的位置)s,如果s>=x1,说明连这个区间最小的x的车都在矩形内部(s<=x2的),这个矩阵一定是被覆盖了的。  反之,这个矩阵就没有被覆盖。

初始化线段树每个点为-1好了!


然后交换x,y扫一遍(相当于是矩阵从上往下扫,因为之前只是从左往右扫)。

只要任意一次扫到该矩阵被覆盖,那该矩阵被覆盖,反之没有。


代码:

#include<cstdio>
#include<iostream>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<queue>
#include<cmath>
using namespace std;
const int maxn=1e5+20;
int n,m,k,q;
bool ok[2*maxn];
struct che
{
	int x,y;
}c[2*maxn];
struct juxing
{
	int x1,y1,x2,y2,id;
}ju[2*maxn];
bool cmp1(che a,che b)
{
	return a.x<b.x;
}
bool cmp2(juxing a,juxing b)
{
	return a.x2<b.x2;
}
struct node
{
	int v,l,r;
}T[4*maxn];
void pushup(int i)
{
	T[i].v=min(T[i<<1].v,T[i<<1|1].v);
}
void build(int i,int l,int r)
{
	T[i].l=l;
	T[i].r=r;
	if(l==r)
	{
		T[i].v=-1;
		return ;
	}
	int mid=(l+r)>>1;
	build(i<<1,l,mid);
	build(i<<1|1,mid+1,r);
	pushup(i);
}
void updata(int i,int x,int v)//change pos[x].key ->v
{
	int l=T[i].l;
	int r=T[i].r;
	if(l==r)
	{
		T[i].v=v;
		return ;
	}
	int mid=(l+r)>>1;
	if(x<=mid)updata(i<<1,x,v);
	else updata(i<<1|1,x,v);
	pushup(i);
}
int query(int i,int L,int R)
{
	int l=T[i].l;
	int r=T[i].r;
	if(l>=L&&r<=R)return T[i].v;
	int mm=0x3f3f3f3f;
	int mid=(l+r)>>1;
	if(L<=mid)mm=min(mm,query(i<<1,L,R));
	if(R>mid)mm=min(mm,query(i<<1|1,L,R));
	return mm;
}
void work()
{
	build(1,1,maxn);
	int tl=1;//当前计算车的指针 
	for(int i=1;i<=q;i++)//枚举每个矩形 
	{
		int tp=ju[i].x2;//最右边
		while(c[tl].x<=tp&&tl<=k)//加入车 
		{
			updata(1,c[tl].y,c[tl].x);
			tl++;
		} 
		//加入了所有车
		int dk=query(1,ju[i].y1,ju[i].y2);//车存在x的最小值
		if(dk<ju[i].x1)continue;
		ok[ju[i].id]=true;
	}
}
int main()
{
	freopen("brother.in","r",stdin);
	freopen("brother.out","w",stdout);
	scanf("%d%d%d%d",&n,&m,&k,&q);
	for(int i=1;i<=k;i++)scanf("%d%d",&c[i].x,&c[i].y);
	for(int i=1;i<=q;i++)
	{
		scanf("%d%d%d%d",&ju[i].x1,&ju[i].y1,&ju[i].x2,&ju[i].y2);
		ju[i].id=i;
	}
	sort(c+1,c+k+1,cmp1);
	sort(ju+1,ju+q+1,cmp2);
	
	work();
	for(int i=1;i<=k;i++)swap(c[i].x,c[i].y);
	for(int i=1;i<=q;i++)
	{
		swap(ju[i].x1,ju[i].y1);
		swap(ju[i].x2,ju[i].y2);
	}
	sort(c+1,c+k+1,cmp1);
	sort(ju+1,ju+q+1,cmp2);
	
	work();
	
	for(int i=1;i<=q;i++)
	{
		if(ok[i])printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值