一个线段树板子题。
一.题意
有一个n*m的格子空间,现在你有一个机器人放在位置1,你想让他到达位置2,你每次可以向它发出向上向下向左向右四个指令,但是这个机器人他坏了,所以你每次发出指令后,他都会机械地执行你的指令k次,中途不可以暂停,这n*m的格子空间,每一列格子下面都有一定高度的格子是堵塞的,如果机器人走到堵塞的格子或者跑到墙壁外面的话,那这个机器人就会爆炸,问你能不能在机器人不爆炸的前提下给它发出指令让它从位置1走到位置2。
给到你行列,还有每列堵塞的格子有多高(因为从下往上堵),然后就是查询了,每个查询给你位置1和位置2还有每次机械执行的次数k。
二.思路
想要能够到达终止位置的话必定满足以下性质。
1.起始位置纵坐标与终止位置纵坐标之差的绝对值必须是k的整数。起始位置横坐标与终止位置横坐标之差的绝对值必须是k的整数。
2.必须能绕过他们之间最高的那列堵塞(利用线段树查询区间最大值的特性,找到哪个最大,然后二分机器人走几次k能第一次到达比最大堵塞高的位置,如果这个能到达的第一个比最大堵塞高的位置超出了墙壁,那就不行)
看到这里快要结束啦,很开心你可以点进来,如果文章对你有帮助的话,可以顺手点一下下面的小手手👍吗?你的支持是对我创作最大的鼓励 !
三.代码
#include <iostream>
#include <cstring>
#include <algorithm>
#include <map>
#include <cmath>
#include<cstdio>
using namespace std;
const int N = 2e5+10;
typedef long long LL;
const int maxn=2e6+10;
int a[maxn],segMax[maxn*4];//线段树数组要开大4倍
void build(int cur,int L,int R)
{//cur表示区间[L,R]的标号
if(L==R) segMax[cur]=a[L];
else
{
int mid=(L+R)/2;
build(cur*2,L,mid);
build(cur*2+1,mid+1,R);
segMax[cur]=max(segMax[cur*2],segMax[cur*2+1]);
}
}
int get_max(int cur,int L,int R,int x,int y)
{ //查询区间[x,y]的最大值
if(x>R||y<L) return -0x3f3f3f3f; //求最大值,此时设置返回负无穷大
if(x<=L&&y>=R) return segMax[cur];
int mid=(L+R)/2;
return max(get_max(cur*2,L,mid,x,y),get_max(cur*2+1,mid+1,R,x,y));
}
void solve()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=m;i++)
{
cin>>a[i];
}
build(1,1,m);
int q;
cin>>q;
while(q--)
{
LL x1,y1,x2,y2;
LL k;
scanf("%lld%lld%lld%lld%lld",&x1,&y1,&x2,&y2,&k);
if(abs(x2-x1)%k!=0||abs(y2-y1)%k!=0)//性质1
{
puts("NO");
continue;
}
LL maxx=get_max(1,1,m,min(y1,y2),max(y1,y2));//线段树查询区间最大值
LL l=0,r=1e10;
while(l<r)
{
LL mid=(l+r)>>1;
if((LL)(mid*k+(LL)min(x1,x2))>maxx) r=mid;
else l=mid+1;
}
if(r*k+min(x1,x2)>n||r*k+min(x1,x2)<=maxx)//①超出墙壁②跳不过最高堵塞
{
puts("NO");
continue;
}
puts("YES");
}
}
int main()
{
int T;
T=1;
while(T--)
{
solve();
}
}