APIO2018 新家

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

posted @ 2018-11-05 16:08  Apocrypha  阅读(436)  评论(0编辑  收藏  举报