CDOJ 1071 秋实大哥下棋

3 篇文章 0 订阅

秋实大哥下棋

limit 1s 65535KB

胜负胸中料已明,又从堂上出奇兵。秋实大哥是一个下棋好手,独孤求败的他觉得下棋已经无法满足他了,他开始研究一种新的玩法。
在一个n×m的棋盘上,放置了k个车,并且他在棋盘上标出了q个矩形,表示矩形内部是战略要地。秋实大哥要求一个矩形内的每一个格子,都至少能被一辆在矩形内的车攻击到,那么这个矩形就是被完整保护的。
现在秋实大哥想知道每一个矩形是否被完整保护。


input

第一行包含四个整数n,m,k,q,表示棋盘的大小,车的数量以及矩形的数量。
接来下k行,每行包含两个整数x,y,表示有一辆车位于从左往右第x列,从下往上第y行。
接下来q行,每行包含四个整数x1,y1,x2,y2,表示一个矩形的左下角和右上角坐标。
1≤n,m≤105,1≤k,q≤2⋅105,1≤x1≤x2≤105,1≤y1≤y2≤105,1≤x≤105,1≤y≤105。

output

输出q行,对于每一次询问,这个矩形若被完整保护了输出"YES",否则输出"NO"。

sameple input

4 3 3 3
1 1
3 2
2 3
2 3 2 3
2 1 3 3
1 2 2 3
sameple output

YES
YES
NO


题解

好难啊,完全不会

所以看题解,问大神,折腾了好久终于敢自己写一发了……

竟然一发过,开心……


但是题解很不好表述啊,怎么说,X,Y分开进行,比如X轴方向扫描,在Y轴上建线段树,线段树维护[y1,y2]区间内车的X的最小值。

每次扫描到一个矩形的右边,就询问[y1,y2]区间中车的X的最小值,如果X小于该矩形的左边,说明纵坐标在[Y1,Y2]区间的车至少一辆不在矩形内,覆盖失败

Y轴一样

#include<bits/stdc++.h>
#include<vector>

using namespace std;
const int maxn=1e5;
const int max_size=1<<18;
const int inf=1e7;

struct Carriage
{
	int x;
	int y;
};
Carriage car[2*maxn+5];

struct Retangular
{
	int x1,x2,y1,y2;
};
Retangular ret[2*maxn+5];

vector <int> x_car[maxn+5];
vector <int> y_car[maxn+5];
vector <int> rside[maxn+5];
vector <int> hside[maxn+5];

int x_tree[max_size+5];
int y_tree[max_size+5];
int ans[2*maxn+5];

void update(int tree[],int x,int m,int sz)
{
	x+=sz-1;
	tree[x]=m;
	x>>=1;
	while (x>=1)
	{
		tree[x]=min(tree[x<<1],tree[(x<<1)+1]);
		x>>=1;
	}
}
int query(int tree[],int a,int b,int k,int l,int r)
{
	if (a<=l && r<=b) return tree[k];
	else if (r<a || b<l) return inf;
	else
	{
		int mid=(l+r)/2;
		return min(query(tree,a,b,k<<1,l,mid),query(tree,a,b,(k<<1)+1,mid+1,r));
	}
}

void y_solve(int n,int sz_y)
{
	for (int i=1;i<=n;++i)
	{
		if (!x_car[i].empty())
		{
			for (int j=0;j<x_car[i].size();++j)
			{
				int t=x_car[i][j];
				update(y_tree,car[t].y,car[t].x,sz_y);
			}
		}
		if (!rside[i].empty())
		{
			for (int j=0;j<rside[i].size();++j)
			{
				int t=rside[i][j];
				if (query(y_tree,ret[t].y1,ret[t].y2,1,1,sz_y)<ret[t].x1) 
				{
					ans[t]+=1;
				}
			}
		}
	}
	
}

void x_solve(int m,int sz_x)
{
	for (int i=1;i<=m;++i)
	{
		if (!y_car[i].empty())
		{
			for (int j=0;j<y_car[i].size();++j)
			{
				int t=y_car[i][j];
				update(x_tree,car[t].x,car[t].y,sz_x);
			}
		}
		if (!hside[i].empty())
		{
			for (int j=0;j<hside[i].size();++j)
			{
				int t=hside[i][j];
				if (query(x_tree,ret[t].x1,ret[t].x2,1,1,sz_x)<ret[t].y1) 
				{
					ans[t]+=1;
				}
			}
		}
	}
	
} 
int main(void)
{
	#ifdef ex
	freopen ("in.txt","r",stdin);
	#endif
	
	int n,m,k,q;
	int x,y,x1,x2,y1,y2;
	
	scanf("%d%d%d%d",&n,&m,&k,&q);
	
	int sz_x=1;
	while (sz_x<n) sz_x<<=1;
	int sz_y=1;
	while (sz_y<m) sz_y<<=1;
	
	for (int i=1;i<=k;++i)
	{
		scanf("%d%d",&x,&y);
		car[i].x=x;
		car[i].y=y;
		x_car[x].push_back(i);
		y_car[y].push_back(i);
	}
	
	for (int i=1;i<=q;++i)
	{
		scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
		ret[i].x1=x1;
		ret[i].x2=x2;
		ret[i].y1=y1;
		ret[i].y2=y2;
		
		rside[x2].push_back(i);
		hside[y2].push_back(i);
	}
	#ifdef ex1
	if (x_solve(m,sz_x) && y_solve(n,sz_y))
	{
		printf("YES\n");
	}
	else
	{
		printf("NO\n");
	}
	#endif
	
	x_solve(m,sz_x);
	y_solve(n,sz_y);
	
	for (int i=1;i<=q;++i)
	{
		if (ans[i]==2) printf("NO\n");
		else
		{
			printf("YES\n");
		}
	}
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值