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;
}