APIO2018 新家

mac2022-06-30  125

目录

APIO2018 新家题意题解Code:

APIO2018 新家

题目传送门

题意

五福街是一条笔直的道路,这条道路可以看成一个数轴,街上每个建筑物的坐标都可以用一个整数来表示。小明是一位时光旅行者,他知道在这条街上,在过去现在和未来共有 \(n\) 个商店出现。第 \(i\) 个商店可以使用四个整数 \(x_i\), \(t_i\), \(a_i\), \(b_i\)描述,它们分别表示:商店的坐标、商店的类型、商店开业的年份、商店关闭的年份。

小明希望通过时光旅行,选择一个合适的时间,住在五福街上的某个地方。他给出了一份他可能选择的列表,上面包括了 \(q\) 个询问,每个询问用二元组(坐标,时间)表示。第 \(i\) 对二元组用两个整数 \(l_i\), \(y_i\)描述,分别表示选择的地点 \(l_i\)和年份\(y_i\)

现在,他想计算出在这些时间和地点居住的生活质量。他定义居住的不方便指数为:在居住的年份,离居住点最远的商店类型到居住点的距离。类型 \(t\) 的商店到居住点的距离定义为:在指定的年份,类型 \(t\) 的所有营业的商店中,到居住点距离最近的一家到居住点的距离。我们说编号为 \(i\) 的商店在第 \(y\) 年在营业当且仅当 \(a_i \leq y \leq b_i\)。注意,在某些年份中,可能在五福街上并非所有 \(k\) 种类型的商店都有至少一家在营业。在这种情况下,不方便指数定义为−1。你的任务是帮助小明求出每对(坐标,时间)二元组居住的不方便指数。\((1 \leq n,q \leq 3*10^5 , 1 \leq x_i,a_i,b_i \leq 10^9 , 1 \leq t_i \leq k , a_i \leq b_i , 1 \leq l_i,y_i \leq 10^8)\)

题解

首先很容易想到的是我们可以把一个商店在时间\([l,r]\)营业改为在时间\(l\)处插入和在时间\(r\)处删除。这样我们就有了一个大致的算法:将所有的操作按照时间排序,然后顺序地按照时间进行插入,并用某种数据结构动态维护贡献,最后对于每组询问输出答案。 然后我们考虑如何去维护这个贡献。首先无解的情况是比较好判的,我们就可以直接略过了。然后我们考虑这个答案的计算方法,实际上就是找一段最短的长度\(ans\),使得\([x-ans,x+ans]\)这段区间中涵盖了所有的商店,这个显然是可以二分答案来解决的。于是问题就转化成为了二分答案之后查询区间不同的数字是否等于\(k\)。然后继续转化问题,我们记录每一个商店的前驱(即这一种商店上一次出现的位置)如果一种商店在区间\([l,r]\)中出现了,并且这种商店也在\([r+1,inf]\)中出现了,那么这种商店在\([r+1,inf]\)这段区间中,每一个数的前驱必定\(\geq l\),如果在\([l,r]\)中没有出现过,那么在\([r+1,inf]\)区间里面,前驱的最小值一定\(\leq l\)。知道了这个,我们相当于吧问题转化成为了区间询问前驱的最小值。这样实际上我们就已经可以做掉这道题目了,开一个\(set\)维护每一个商店的前驱(为了方便,在0和inf的两个位置都插入所有种类的商店),然后开一个线段树维护前驱的最小值,每次二分答案之后在线段树上\(Check\),总共的复杂度为\(O(nlog^2(n))\)。 然后实际上这题还可以继续优化,我们会发现我们在计算答案的时候,即用到了二分,又用到了线段树。然而实际上,线段树和二分往往是可以并在一起的(雾,然后我们就可以在线段树上直接进行二分。原本二分的时候,我们如果即\([i,inf]\)这段区间的前驱最小值为\(Mn\),相当于是在找一个最大的位置\(i\),满足\(Mn+i \leq 2*x\),所以我们直接在线段树上二分即可,方法是这样的: 假设当前区间为\([l,r]\)

如果\(x\)\([mid+1,r]\)的范围内,那么说明\(i\)也一定在\([mid+1,r]\)范围内,直接递归右子树即可。如果\(x\)\([l,mid]\)的范围内,那么\(i\)在左右两个区间都有可能存在,然后判断当前的\(mid+1\)是否满足\(Mn+i \leq 2*x\)的条件,如果满足则往右子树递归,否则往左子树递归。 这样做的复杂度就是\(O(nlog(n))\)的了。

Code:

#include<bits/stdc++.h> using namespace std; const int N=3e5+500; const int Inf=1e9+7; int n,nq,tot,c,sptot,k; int srt[N<<2],cnt[N<<1]; multiset<int>S[N<<1]; typedef multiset<int>::iterator Sit; struct Opt { int opt; int x,t,tp,idx,ans; bool operator < (const Opt &rhs) const { return t==rhs.t?opt<rhs.opt:t<rhs.t; } }p[N<<2]; bool Cmp(Opt a,Opt b) { return a.idx<b.idx; } struct Set { priority_queue<int,vector<int>,greater<int> >Q,Del; int Top() { while(!Del.empty()&&Q.top()==Del.top()) { Q.pop();Del.pop(); } return Q.empty()?c+1:Q.top(); } }q[N<<1]; namespace Seg { int Mn[N<<4],pos[N<<4]; #define ls(o) o<<1 #define rs(o) o<<1|1 void Build(int o,int l,int r) { Mn[o]=Inf; if(l==r) { pos[o]=srt[l]; return ; } int mid=(l+r)>>1; Build(ls(o),l,mid);Build(rs(o),mid+1,r); pos[o]=pos[ls(o)]; } void Modify(int o,int l,int r,int ps,int v) { if(l==r) { Mn[o]=srt[v]; return ; } int mid=(l+r)>>1; if(ps<=mid) Modify(ls(o),l,mid,ps,v); else Modify(rs(o),mid+1,r,ps,v); Mn[o]=min(Mn[ls(o)],Mn[rs(o)]); return ; } int Query(int o,int l,int r,int ps) { if(l==r) return Mn[o]; int mid=(l+r)>>1; if(ps>mid) return Query(rs(o),mid+1,r,ps); else return min(Mn[rs(o)],Query(ls(o),l,mid,ps)); } int Ask(int ps) { ps=srt[ps]; int l=0,r=c+1; int o=1; while(l<r) { int mid=(l+r)>>1; if(ps>=pos[rs(o)]) o=rs(o),l=mid+1; else { if(pos[rs(o)]+Mn[rs(o)]+1<=2*ps) o=rs(o),l=mid+1; else o=ls(o),r=mid; } } int Min=Query(1,0,c+1,l+1); int ret=max(srt[l],2*ps-Min); return ret-ps; } } void Init() { c=0; for(int i=1;i<=tot;i++) { srt[++c]=p[i].x; } sort(srt+1,srt+1+c); c=unique(srt+1,srt+1+c)-srt-1; srt[0]=-Inf,srt[c+1]=Inf; for(int i=1;i<=tot;i++) { p[i].x=lower_bound(srt+1,srt+1+c,p[i].x)-srt; } sort(p+1,p+1+tot); for(int i=1;i<=k;i++) { S[i].insert(0); S[i].insert(c+1); q[c+1].Q.push(0); } Seg::Build(1,0,c+1); Seg::Modify(1,0,c+1,c+1,0); } void Insert(int ps,int tp) { cnt[tp]++;sptot+=cnt[tp]==1; Sit it=S[tp].insert(ps); Sit nw=it;it--; q[ps].Q.push(*it); Seg::Modify(1,0,c+1,ps,q[ps].Top()); q[*(++nw)].Del.push(*it); q[*(nw)].Q.push(ps); Seg::Modify(1,0,c+1,*(nw),q[*nw].Top()); return ; } void Delete(int ps,int tp) { cnt[tp]--;sptot-=cnt[tp]==0; Sit it=S[tp].find(ps); Sit tmp=it,nw=it;it--; q[ps].Del.push(*it); Seg::Modify(1,0,c+1,ps,q[ps].Top()); q[*(++nw)].Del.push(*tmp); q[*nw].Q.push(*it); Seg::Modify(1,0,c+1,*(nw),q[*nw].Top()); S[tp].erase(tmp); } int main() { scanf("%d%d%d",&n,&k,&nq); for(int i=1,x,t,a,b;i<=n;i++) { scanf("%d%d%d%d",&x,&t,&a,&b); p[++tot].opt=-1;p[tot].x=x;p[tot].tp=t;p[tot].t=a;p[tot].idx=Inf; p[++tot].opt=1;p[tot].x=x;p[tot].tp=t;p[tot].t=b;p[tot].idx=Inf; } for(int i=1,l,y;i<=nq;i++) { scanf("%d%d",&l,&y); p[++tot].opt=0;p[tot].x=l;p[tot].t=y;p[tot].idx=i; } Init(); for(int i=1;i<=tot;i++) { if(p[i].opt==-1) Insert(p[i].x,p[i].tp); else if(p[i].opt==0) { if(sptot!=k) p[i].ans=-1; else p[i].ans=Seg::Ask(p[i].x); } else Delete(p[i].x,p[i].tp); } sort(p+1,p+1+tot,Cmp); for(int i=1;i<=nq;i++) { printf("%d\n",p[i].ans); } return 0; }

转载于:https://www.cnblogs.com/Apocrypha/p/9909669.html

相关资源:JAVA上百实例源码以及开源项目
最新回复(0)