[APIO2018] New Home 新家 题解

89 篇文章 1 订阅
该博客介绍了如何使用离散化线段树解决一个复杂的问题:在一个动态变化的数轴上,每个点都有特定颜色,需要在任何时刻查询某个颜色点的最远距离。博客详细阐述了如何构建线段树,进行二分查找,并通过维护前驱点信息优化查询。还提供了两种不同复杂度的解决方案,分别是O(N×log²N)和O(N×logN),并给出了完整的C++代码实现。
摘要由CSDN通过智能技术生成

[APIO2018] New Home 新家 题解

传送门

首先将时间离散化,然后依据时间建一棵线段树。将每一个店开业歇业时间搞到树上。

这样问题就转化为:有一个数轴每次加入或删除一个点。每一个点有一个颜色。在任意时间询问某一个点距离某一种颜色的最远距离。到某一个颜色的距离为到这个颜色的点的最小距离。

显然可以二分答案。每次询问区间 [ l , r ] [l,r] [l,r]是否包含了所有颜色的点。

对于每一个颜色我们搞一个set。记录每一个点到前面的那个同色的点,也就是前驱,记为pre[i]。

这个玩意的充要条件是在区间 [ l , ∞ ] [l,\infin] [l,] 的pre[i]都满足>=r。

对于这个我们也可以将坐标离散化搞到一个树上。

时间复杂度是 O ( N × log ⁡ 2 N ) O(N\times \log ^2 N) O(N×log2N)

这题也有单log的做法,就是在线段树上二分的地方的优化,不想多讲。

code:

(这个代码被卡常了我也懒得改了)

#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
inline int read()
{
	int num = 0, f = 1;
	char ch = getchar();
	while( !isdigit( ch ) ) { if(ch == '-') f = -1; ch = getchar(); }
	while( isdigit( ch ) ) num = (num << 3) + (num << 1) + (ch ^ 48), ch = getchar();
	return num * f;
}
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/ 
const int MAXN=6e5+20;
int n,k,q;
vector<mp> sor; 
vector<int> pos_;
int x[MAXN],t[MAXN],a[MAXN],b[MAXN],l[MAXN],y[MAXN];
map<int,int> M;
int find(int f){
	//第一个>=f的位置
	return lower_bound(ALL(pos_),f)-pos_.begin()+1; 
}
const int N=1<<20;
int rest[MAXN];

struct SEGMENTTREE{
	int tree[N+N];//最小 
	SEGMENTTREE(){
		memset(tree,63,sizeof(tree));
	} 
	void modify(int index,int val){
		index+=N-1;
		tree[index]=val;
		index>>=1;
		while(index){
			tree[index]=min(tree[index<<1],tree[index<<1|1]);
			index>>=1;
		}
	}
	int query(int a,int b,int now=1,int l=1,int r=N+1){
		if(r<=a||l>=b){
			return INF;
		}
		if(r<=b&&l>=a){
			return tree[now];
		}
		int mid=(l+r)>>1;
		return min(query(a,b,now<<1,l,mid),query(a,b,now<<1|1,mid,r));
	}
}sgt;
set<int> pos[MAXN];
void add(int x_,int t_){
	pos[t_].insert(x_);
	auto ite=pos[t_].upper_bound(x_);
	if(ite!=pos[t_].end()){
		sgt.modify(*ite,x_);
	}
	ite=pos[t_].lower_bound(x_);
	if(ite!=pos[t_].begin()){
		ite--;
		sgt.modify(x_,*ite);
	}
	else {
		sgt.modify(x_,-INF);
	}
}
void del(int x_,int t_){
	sgt.modify(x_,INF);
	pos[t_].erase(x_);
	auto ite=pos[t_].upper_bound(x_);
	if(ite==pos[t_].end()) return;
	if(ite==pos[t_].begin()){
		sgt.modify(*ite,-INF);
	}
	else{
		int z=*ite;
		ite--;
		sgt.modify(z,*ite);
	}
}
bool check(int x_,int dis){
	int st=find(x_+dis+1);
	if(sgt.query(st,N+1)<find(x_-dis)) return 0;
	return 1;
}
struct EVENTSEGTREE{
	vector<int> tree[N+N];
	EVENTSEGTREE(){
		
	}
	void add_event(int st,int ed,int val,int now=1,int l=1,int r=N+1){
		if(r<=st||l>=ed){
			return ;
		}
		if(r<=ed&&l>=st){
			tree[now].PB(val);
			return ;
		}
		int mid=(l+r)>>1;
		add_event(st,ed,val,now<<1,l,mid);
		add_event(st,ed,val,now<<1|1,mid,r);
	}
	
	void run(int now=1,int l_=1,int r_=N+1){
		for(auto it:tree[now]){
			if(it>0){
				add(x[it],t[it]);
			}	
		}
		if(l_==r_-1){
			for(auto it:tree[now]){
				if(it<0){
					int lb=0,rb=1e8+1;
					if(!check(l[-it],rb)){
						rest[-it]=-1;
					}
					else{
						while(lb<rb){
							int mid=(lb+rb)>>1;
							if(check(l[-it],mid)) rb=mid;
							else lb=mid+1;
						}
						rest[-it]=lb;
					}
				}
			}
		}
		else{
			int mid=(l_+r_)>>1;
			run(now<<1,l_,mid);
			run(now<<1|1,mid,r_);
		}
		for(auto it:tree[now]){
			if(it>0){
				del(x[it],t[it]);
			}
		}
	}
}esgt;
int main(){
//	scanf("%d%d%d",&n,&k,&q);
	n=read();
	k=read();
	q=read();
	rb(i,1,n){
		int xi,ti,ai,bi;
//		scanf("%d%d%d%d",&xi,&ti,&ai,&bi);
		xi=read();
		ti=read();
		ai=read();
		bi=read();
		a[i]=ai;
		b[i]=bi;
		M[ai]=M[bi]=1;
		t[i]=ti;
		sor.PB(II(xi,i));
	}	
	rb(i,1,q){
		l[i]=read();
		y[i]=read();
	}
	rb(i,1,q){
//		scanf("%d%d",&l[i],&y[i]);
		M[y[i]]=1;
	}
	rb(i,1,k){
		n++;
		a[n]=-INF;
		b[n]=INF;
		M[a[n]]=M[b[n]]=1;
		x[n]=INF;
		t[n]=i;
		sor.PB(II(x[n],n));
	}
	sort(ALL(sor));
	rep(i,n){
		x[sor[i].SEC]=i+1;
		pos_.PB(sor[i].FIR);
	}
	int cnt=0;
	for(auto ite=M.begin();ite!=M.end();ite++){
		ite->SEC=++cnt;
	}
	rb(i,1,n){
		a[i]=M[a[i]];
		b[i]=M[b[i]];
		esgt.add_event(a[i],b[i]+1,i);
	}
	rb(i,1,q){
		y[i]=M[y[i]];
		esgt.add_event(y[i],y[i]+1,-i);
	}
	esgt.run();
	rb(i,1,q){
		printf("%d\n",rest[i]);
	}
	return 0;
}

UPDATE:

还是补充一下 O ( N × log ⁡ N ) O(N\times \log N) O(N×logN)的做法吧,(UOJ 已ac!)

#include<bits/stdc++.h>
#define rb(a,b,c) for(int a=b;a<=c;++a)
#define rl(a,b,c) for(int a=b;a>=c;--a)
#define LL long long
#define IT iterator
#define PB push_back
#define II(a,b) make_pair(a,b)
#define FIR first
#define SEC second
#define FREO freopen("check.out","w",stdout)
#define rep(a,b) for(int a=0;a<b;++a)
#define SRAND mt19937 rng(chrono::steady_clock::now().time_since_epoch().count())
#define random(a) rng()%a
#define ALL(a) a.begin(),a.end()
#define POB pop_back
#define ff fflush(stdout)
#define fastio ios::sync_with_stdio(false)
#define check_min(a,b) a=min(a,b)
#define check_max(a,b) a=max(a,b)
using namespace std;
inline int read()
{
	int num = 0, f = 1;
	char ch = getchar();
	while( !isdigit( ch ) ) { if(ch == '-') f = -1; ch = getchar(); }
	while( isdigit( ch ) ) num = (num << 3) + (num << 1) + (ch ^ 48), ch = getchar();
	return num * f;
}
const int INF=0x3f3f3f3f;
typedef pair<int,int> mp;
/*}
*/ 
const int MAXN=6e5+20;
int n,k,q;
vector<mp> sor; 
vector<int> pos_;
int x[MAXN],t[MAXN],a[MAXN],b[MAXN],l[MAXN],y[MAXN];
map<int,int> M;
int find(int f){
	//第一个>=f的位置
	return lower_bound(ALL(pos_),f)-pos_.begin()+1; 
}
const int N=1<<20;
int rest[MAXN];

struct SEGMENTTREE{
	int tree[N+N];//最小 
	SEGMENTTREE(){
		memset(tree,63,sizeof(tree));
	} 
	void modify(int index,int val){
		index+=N-1;
		tree[index]=val;
		index>>=1;
		while(index){
			tree[index]=min(tree[index<<1],tree[index<<1|1]);
			index>>=1;
		}
	}
	int query(int a,int b,int now=1,int l=1,int r=N+1){
		if(r<=a||l>=b){
			return INF;
		}
		if(r<=b&&l>=a){
			return tree[now];
		}
		int mid=(l+r)>>1;
		return min(query(a,b,now<<1,l,mid),query(a,b,now<<1|1,mid,r));
	}
	int low_bound(int pos,int now=1,int l=1,int r=N+1,int MIN=INF){
		if(l==r-1) {
			int ret=INF;
			ret=min(ret,pos_[l-1]-pos);
			if(min(tree[now],MIN)>0)
			ret=min(ret,pos-pos_[min(tree[now],MIN)-1]);
			return ret;	
		}
		int mid=(l+r)>>1;
		if(mid>pos_.size()) return low_bound(pos,now<<1,l,mid,min(MIN,tree[now<<1|1]));
		if(pos_[mid-1]<=pos) return low_bound(pos,now<<1|1,mid,r,MIN);
		if(min(tree[now<<1|1],MIN)!=-INF&&(pos_[mid-2]-pos>=pos-pos_[min(tree[now<<1|1],MIN)-1])){
			return low_bound(pos,now<<1,l,mid,min(MIN,tree[now<<1|1]));
		}
		return low_bound(pos,now<<1|1,mid,r,MIN);
	}
}sgt;
set<int> pos[MAXN];
void add(int x_,int t_){
	pos[t_].insert(x_);
	auto ite=pos[t_].upper_bound(x_);
	if(ite!=pos[t_].end()){
		sgt.modify(*ite,x_);
	}
	ite=pos[t_].lower_bound(x_);
	if(ite!=pos[t_].begin()){
		ite--;
		sgt.modify(x_,*ite);
	}
	else {
		sgt.modify(x_,-INF);
	}
}
void del(int x_,int t_){
	sgt.modify(x_,INF);
	pos[t_].erase(x_);
	auto ite=pos[t_].upper_bound(x_);
	if(ite==pos[t_].end()) return;
	if(ite==pos[t_].begin()){
		sgt.modify(*ite,-INF);
	}
	else{
		int z=*ite;
		ite--;
		sgt.modify(z,*ite);
	}
}
bool check(int x_,int dis){
	int st=find(x_+dis+1);
	if(sgt.query(st,N+1)<find(x_-dis)) return 0;
	return 1;
}
int main(){
//	scanf("%d%d%d",&n,&k,&q);
	n=read();
	k=read();
	q=read();
	rb(i,1,n){
		int xi,ti,ai,bi;
//		scanf("%d%d%d%d",&xi,&ti,&ai,&bi);
		xi=read();
		ti=read();
		ai=read();
		bi=read();
		a[i]=ai;
		b[i]=bi;
		M[ai]=M[bi]=1;
		t[i]=ti;
		sor.PB(II(xi,i));
	}	
	rb(i,1,q){
		l[i]=read();
		y[i]=read();
	}
	rb(i,1,q){
//		scanf("%d%d",&l[i],&y[i]);
		M[y[i]]=1;
	}
	rb(i,1,k){
		n++;
		a[n]=-INF;
		b[n]=INF;
		M[a[n]]=M[b[n]]=1;
		x[n]=INF;
		t[n]=i;
		sor.PB(II(x[n],n));
	}
	sort(ALL(sor));
	rep(i,n){
		x[sor[i].SEC]=i+1;
		pos_.PB(sor[i].FIR);
	}
	int cnt=0;
	for(auto ite=M.begin();ite!=M.end();ite++){
		ite->SEC=++cnt;
	}
	vector<mp> eve;
	rb(i,1,n){
		a[i]=M[a[i]];
		b[i]=M[b[i]];
		eve.PB(II(a[i],i-INF));
		eve.PB(II(b[i]+1,i));
	}
	rb(i,1,q){
		y[i]=M[y[i]];
		eve.PB(II(y[i],i+INF));
	}
	sort(ALL(eve));
	for(auto &it:eve){
		if(it.SEC<0){
			add(x[it.SEC+INF],t[it.SEC+INF]);
			continue;
		}
		if(it.SEC<INF){
			del(x[it.SEC],t[it.SEC]);
			continue;
		}
		it.SEC-=INF;
		int lb=0,rb=1e8+1;
		if(!check(l[it.SEC],rb)){
			rest[it.SEC]=-1;
		}
		else{
				rest[it.SEC]=sgt.low_bound(l[it.SEC]);
			}
	}
	rb(i,1,q){
		printf("%d\n",rest[i]);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值