2019.03.01【HNOI2018/AHOI2018】【BZOJ5288】【洛谷P4436】游戏(线段树)(单调栈)

BZOJ传送门

洛谷传送门


解析:

听说出题人为了卡住 O ( n log ⁡ 2 n ) O(n\log^2n) O(nlog2n)的做法不小心把 O ( n 2 ) O(n^2) O(n2)暴力放过去了???

那出题人可真是不容易。

然后来说一下 O ( n log ⁡ n ) O(n\log n) O(nlogn)的做法。

考虑处理每个点能够到达的最左最右端点,然后询问就可以 O ( 1 ) O(1) O(1)回答了。

首先我们可以暂时不管钥匙和锁上的门, O ( n ) O(n) O(n)递推处理处所有点可能走到的最左端点。

我们认为,一扇门不可能走到,当且仅当,我们目前和该扇门的钥匙在门的不同两侧。

现在我们设 l i , r i l_i,r_i li,ri为从点 i i i出发,最终能够走到的最左和最右端点。初始化 l i = r i = 1 l_i=r_i=1 li=ri=1,考虑扩展。

我们发现,在所有向左可能走到的门中,只有那些最终钥匙在 r i r_i ri后面的门无论如何都走不过去。

同理,对于右边的门,钥匙在 l i l_i li之前的门是绝对不可能走过去的。

线段树维护所有在 [ l , r ] [l,r] [l,r]中的门的最靠右的钥匙。

考虑倒着处理。

对于 r i r_i ri的更新我们可以用一个单调栈来实现,维护还没有被打开的门的钥匙的位置

显然这样所有 r i r_i ri的更新次数就是 O ( n ) O(n) O(n)的。

而我们只需要在每次更新 r i r_i ri的同时用线段树找到 [ L i , l i ) [L_i,l_i) [Li,li)中最靠左的,钥匙位置 ≤ r i \leq r_i ri的门就可以将 l i l_i li更新过去了。


代码:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc get_char
#define cs const

namespace IO{
	inline char get_char(){
		static cs int Rlen=1<<20|1;
		static char buf[Rlen],*p1,*p2;
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++; 
	}
	
	inline int getint(){
		re char c;
		while(!isdigit(c=gc()));re int num=c^48;
		while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
		return num;
	}
}
using namespace IO;

cs int N=1e6+6;
int n,m,q;
int l[N],r[N],lm[N];
int mx[N<<2],key[N];

inline void build(int k,int l,int r){
	if(l==r)return (void)(mx[k]=key[l]);
	int mid=(l+r)>>1;
	build(k<<1,l,mid);
	build(k<<1|1,mid+1,r);
	mx[k]=max(mx[k<<1],mx[k<<1|1]);
}

inline int query(int k,int l,int r,int val){
	if(mx[k]<=val)return 0;
	if(l==r)return l;
	int mid=(l+r)>>1;
	return (mx[k<<1|1]>val)?query(k<<1|1,mid+1,r,val):query(k<<1,l,mid,val);
}

inline int Query(int k,int l,int r,cs int &ql,cs int &qr,cs int &val){
	if(mx[k]<=val)return 0;
	if(ql<=l&&r<=qr)return query(k,l,r,val);
	int mid=(l+r)>>1,pos=0;
	if(mid<qr)pos=Query(k<<1|1,mid+1,r,ql,qr,val);
	if(pos)return pos;
	if(ql<=mid)return Query(k<<1,l,mid,ql,qr,val);
	return pos;
}

inline int get_pre(int l,int r,int val){
	if(l>r)return l;
	int pos=Query(1,1,n,l,r,val);
	return pos?pos+1:l;
}

int sta[N],top;

inline void work(){
	build(1,1,n);
	key[n]=n+1;
	key[0]=-1;
	for(int re i=1;i<=n;++i)lm[i]=(key[i-1]&&key[i-1]<=i-1)?i:lm[i-1];
	for(int re i=n;i;--i){
		l[i]=r[i]=i;
		l[i]=get_pre(lm[i],l[i]-1,r[i]);
		sta[++top]=i;
		while(top&&((l[i]<=key[sta[top]]&&key[sta[top]]<=r[i])||!key[sta[top]])){
			r[i]=sta[--top];
			l[i]=get_pre(lm[i],l[i]-1,r[i]);
		}
	}
}

signed main(){
	n=getint(),m=getint(),q=getint();
	for(int re i=1;i<=m;++i){
		int x=getint(),y=getint();
		key[x]=y;
	}
	work();
	while(q--){
		int s=getint(),t=getint();
		puts(l[s]<=t&&t<=r[s]?"YES":"NO");
	}
	return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip 【备注】 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用!有问题请及时沟通交流。 2、适用人群:计算机相关专业(如计科、信息安全、数据科学与大数据技术、人工智能、通信、物联网、自动化、电子信息等)在校学生、专业老师或者企业员工下载使用。 3、用途:项目具有较高的学习借鉴价值,不仅适用于小白学习入门进阶。也可作为毕设项目、课程设计、大作业、初期项目立项演示等。 4、如果基础还行,或热爱钻研,亦可在此项目代码基础上进行修改添加,实现其他不同功能。 欢迎下载!欢迎交流学习!不清楚的可以私信问我! 毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip毕设新项目-基于Java开发的智慧养老院信息管理系统源码+数据库(含vue前端源码).zip
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值