【cdoj 1071】秋实大哥下棋 扫描线+线段树+脑洞

首先,我们注意到就是题目中所说的,只有在矩形内部的车才能够保护矩形内部的各个点,这就说明,我们需要在进入矩阵和离开矩阵的时候要分别判断,而这就是一个相当大的扫描线的提示。

先分析一边的情况,我们以x坐标从左到右开始扫,如果一个矩形能够被车保护,那么他的右边到左边之间的所有车能够把这个矩阵的上边到下边之间的纵坐标全部覆盖完,也就是说现在需要处理两个东西,一个是是否覆盖完,一个是覆盖完所用的车中的最小横纵标(如果连最小横坐标都大于它的左边,那么所用的车一定在左右边之间),而这些东西都可以用一颗线段树全部解决,线段树直接维护扫描线上的最小横坐标,如果没有覆盖,最小横坐标小于矩阵左边横坐标


那么也有可能是上下啊,没关系,把所有横纵坐标swap一次,再原封不动的做一次就过了。

注意,swap后重新排序,一开始我忘了wa了一次还是第一组

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define maxn 100006*2
#define ls u<<1,l,mid
#define rs u<<1|1,mid+1,r
using namespace std;
int n,m,k,q,vis[maxn],cnt,mx,my;

struct CAR{
	int x,y;
	bool operator <(const CAR& b)const{return x==b.x?y>b.y:x<b.x;}
}car[maxn];
struct edge{
	int rx,ry,id,lx,ly;
	bool operator<(const edge& b)const{return rx==b.rx?ry>b.ry:rx<b.rx;}
}e[maxn];
struct node{
	int l,r,Min;
}nod[100005*4];

void push_up(int u){nod[u].Min=min(nod[u<<1].Min,nod[u<<1|1].Min);}
void build(int u,int l,int r){
	nod[u].l=l,nod[u].r=r;
	if(l==r){
		nod[u].Min=-1;
		return;
	}
	int mid=l+r>>1;
	build(ls);build(rs);
	push_up(u);
}
void updata(int u,int l,int r,int x,int add){
	if(l==r){nod[u].Min=add;return ;}
	int mid=l+r>>1;
	if(x>mid)updata(rs,x,add);
	else updata(ls,x,add);
	push_up(u);
}
int query(int u,int l,int r,int x,int y){
	if(x==l&&r==y)return nod[u].Min;
	int mid=l+r>>1;
	if(x>mid)return query(rs,x,y);
	else if(y<=mid)return query(ls,x,y);
	else return min(query(ls,x,mid),query(rs,mid+1,y));
}

void find(){
	int pre=1;
	build(1,1,mx);
	for(int i=1;i<=q;i++){
		while(car[pre].x<=e[i].rx&&pre<=k){
			updata(1,1,mx,car[pre].y,car[pre].x);
			pre++;
		}
		int Min=query(1,1,mx,e[i].ly,e[i].ry);
		if(Min>=e[i].lx)vis[e[i].id]=1;
	}
}

int main(){
	
	scanf("%d%d%d%d",&n,&m,&k,&q);
	for(int i=1;i<=k;i++){
		scanf("%d%d",&car[i].x,&car[i].y);
		mx=max(max(mx,car[i].x),car[i].y);
	}
	for(int i=1;i<=q;i++){
		scanf("%d%d%d%d",&e[i].lx,&e[i].ly,&e[i].rx,&e[i].ry);
		mx=max(mx,max(e[i].lx,e[i].ly));
		mx=max(mx,max(e[i].rx,e[i].ry));
		e[i].id=i;
	}
	mx=100000;
	sort(car+1,car+1+k);sort(e+1,e+1+q);
	find();
	for(int i=1;i<=k;i++)swap(car[i].x,car[i].y);
	for(int i=1;i<=q;i++)swap(e[i].lx,e[i].ly),swap(e[i].rx,e[i].ry);
	sort(car+1,car+1+k);sort(e+1,e+1+q);
	find();
	for(int i=1;i<=q;i++){
		if(vis[i])printf("YES\n");
		else printf("NO\n");
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值