牛客2021暑期训练6-H-Hopping Rabbit

牛客2021暑期训练6-H-Hopping Rabbit

题目链接

题意

平面上有 n n n个矩形,给定 d d d,需要找一个位置 ( x , y ) (x,y) (x,y),使得所有 ( x + k d , y + k d ) (x+kd,y+kd) (x+kd,y+kd)均不落在矩形中

题解

前置知识:扫描线
由于能到的位置是重复的,我们将所有矩形移到 ( 0 , 0 ) (0,0) (0,0) ( d , d ) (d,d) (d,d)范围内求并,如果最终范围内存在未被覆盖的点,则该点可作为答案。矩形求并,用扫描线即可。
移动到 ( d , d ) (d,d) (d,d)范围内可能拆成 1 , 2 , 4 1,2,4 1,2,4个矩形
在这里插入图片描述
在这里插入图片描述

同时由于题目给的是矩形,而小兔子是在矩形中点,因此矩形的 x 2 x_2 x2是可以踩的,我们将边也移到中点去, x 1 , x 2 x_1,x_2 x1,x2就变成 x 1 , x 2 − 1 x_1,x_2-1 x1,x21 y y y同理。
其他东西代码里有解释

代码
#include<bits/stdc++.h>
#define PR pair<int,int>
#define Fi first
#define Se second
#define Mp make_pair
#define Pb push_back
using namespace std;
const int N=1e5+9;
int n,d;
int t[N*4],vis[N*4];
vector<PR>e1,e2,s1[N],s2[N];
void Change(int u,int l,int r,int y1,int y2,int flag)
{
	if(y1<=l&&r<=y2)
	{
		t[u]+=flag;
		vis[u]+=flag; //vis表示区间l~r的矩阵个数 
		return;
	}
	int mid=(l+r)/2;
	if(y1<=mid) Change(u*2,l,mid,y1,y2,flag);
	if(y2>mid) Change(u*2+1,mid+1,r,y1,y2,flag);
	t[u]=min(t[u*2],t[u*2+1])+vis[u];
}
int Ask(int u,int l,int r)
{
	if(l==r) return l;
	int mid=(l+r)/2;
	if(t[u]==t[u*2]+vis[u]) return Ask(u*2,l,mid); //23行代码 
	else return Ask(u*2+1,mid+1,r);
}
int main()
{
	cin>>n>>d;
	int P=(1<<30)/d*d; //小于等于 1<<30 的最大d的倍数 
	for(int i=1;i<=n;i++)
	{
		int x1,x2,y1,y2;
		cin>>x1>>y1>>x2>>y2;
		x1+=P; y1+=P; x2+=P; y2+=P; //加上d的倍数保证为正数 
		e1.clear(); e2.clear();
		if(x2-x1>=d) e1.Pb(Mp(0,d-1)); //塞满d 
		else if(x1%d<=(x2-1)%d) e1.Pb(Mp(x1%d,(x2-1)%d)); //x未超过d 
		else //图中超过d,分成两个x 
		{
			e1.Pb(Mp(x1%d,d-1));
			e1.Pb(Mp(0,(x2-1)%d));
		}
		if(y2-y1>=d) e2.Pb(Mp(0,d-1));
		else if(y1%d<=(y2-1)%d) e2.Pb(Mp(y1%d,(y2-1)%d));
		else
		{
			e2.Pb(Mp(y1%d,d-1));
			e2.Pb(Mp(0,(y2-1)%d));
		}
		//只拆成一个矩形:一个x,一个y
		//拆成两个:一个x两个y,或两个x一个
		//拆成四个:两个x,两个y
		for(auto j:e1) //平行y的扫描线 
		{
			for(auto k:e2)
			{
				s1[j.Fi].Pb(k); //前一条y为+1 
				s2[j.Se+1].Pb(k); //j.Fi~j.Se为+1,x=j.Se+1减掉 
			}
		}
	}
	for(int i=0;i<d;i++) //扫描线 
	{
		for(auto j:s1[i])
			Change(1,0,d-1,j.Fi,j.Se,1);
		for(auto j:s2[i])
			Change(1,0,d-1,j.Fi,j.Se,-1);
		if(!t[1]) //若t1==0,说明没有一条线覆盖0~d-1且存在一点为0 
		{
			printf("YES\n%d %d\n",i,Ask(1,0,d-1));
			return 0;
		}
	}
	printf("NO\n");
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值