【loj2585】【APIO2018】新家

题目

一条街上有\(n\) 个点,坐标为\(x_i\) , 店的种类为\(t_i\) , 开业时间为 \([a_i,b_i]\) ;

定义一种类型到一个点的距离为这种类的点到这个点的最近距离 ;

定义一个点的不方便度为此时所有所有种类的店离这个点的距离的最大值(种类不足直接为-1);

给出种类总数和询问数 \(m\) , 回答在 \(y_j\) 时间下,位置 \(l_j\) 的不方便度 ;

$1 \le n , q \le 3 \times 10^5  ,  1 \le k \le n $

$ 1 \le x_i,a_i,b_i \le 10^9  ,  1 \le t_i \le k  ,  a_i \le b_i $

$1 \le l_i  ,  y_i \le 10^8 $

题解

  • 对时间做扫描线,只需要维护支持加入和删除点的查询;

  • 二分答案,考虑在$ [ x - mid , x + mid] $中间是否存在所有颜色的点;

  • \(set\) 维护每种颜色的前驱pre,条件转化成: \((x+mid,inf)\)\(mn \{ pre \} \ge x - mid\)

  • 为了方便你可以设置一个-inf点和inf点,并把所有颜色都先分别扔一个到inf和-inf上 ;

  • 用线段树套堆就可以做到$n  log^2 n $ , 在线段树里面二分 \(x+ans\) 的位置即可做到 $n log  n $ ;

//很清新的数据结构;
#include<bits/stdc++.h>
#define inf (1e9 + 1)
using namespace std;
const int N=300010;
int n,K,q,tot,cnt,ans[N];
multiset<int>s[N],ss[N*64];
multiset<int>::iterator it,itt;
int rt,mn[N*64],ls[N*64],rs[N*64],sz;
struct data{
  int x,c,t,op;
  bool operator <(const data&A)const{return t==A.t?op<A.op:t<A.t;}
}A[N<<2];
char gc(){
  static char*p1,*p2,S[1000000];
  if(p1==p2)p2=(p1=S)+fread(S,1,1000000,stdin);
  return(p1==p2)?EOF:*p1++;
}
int rd(){
  int x=0;char c=gc();
  while(c<'0'||c>'9')c=gc();
  while(c>='0'&&c<='9')x=x*10+c-'0',c=gc();
  return x;
}
void update(int&k,int l,int r,int x,int add,int del){
  if(!k)k=++sz;
  if(l==r){
      if(~add)ss[k].insert(add);
      if(~del)ss[k].erase(ss[k].find(del));
      mn[k]=ss[k].empty() ? inf : *ss[k].begin();//
      return ;
  }
  int mid=(l+r)>>1;
  if(x<=mid)update(ls[k],l,mid,x,add,del);
  else if(x>mid)update(rs[k],mid+1,r,x,add,del);
  mn[k]=min(mn[ls[k]],mn[rs[k]]);
}
int query(int x){
  int k=rt,l=1,r=inf,mnv=inf;//
  while(l<r){
      int mid=(l+r)>>1,tmp=min(mnv,mn[rs[k]]);
      if(mid>=x&&tmp>=2*x-mid)k=ls[k],r=mid,mnv=tmp;
      else k=rs[k],l=mid+1;
  }
  return l - x;
}
int main(){
//    freopen("A.in","r",stdin);
//    freopen("A.out","w",stdout);
  n=rd();K=rd();q=rd();mn[0]=inf;//
  for(int i=1;i<=K;++i)s[i].insert(-inf),s[i].insert(inf);//
  for(int i=1;i<=n;++i){
      int x=rd(),t=rd(),a=rd(),b=rd();
      A[++tot]=(data){x,t,a,0};
      A[++tot]=(data){x,t,b+1,1};
  }
  for(int i=1;i<=q;++i){
      int l=rd(),y=rd();
      A[++tot]=(data){l,i,y,2};
  }
  sort(A+1,A+tot+1);
  for(int i=1;i<=K;++i)update(rt,1,inf,inf,-inf,-1);//
  for(int i=1;i<=tot;++i){
      int opt=A[i].op,c=A[i].c,x=A[i].x;
      if(!opt){
          it=itt=s[c].upper_bound(x);--itt;
          update(rt,1,inf,*it,x,*itt);
          update(rt,1,inf,x,*itt,-1);
          if(s[c].size()==2)cnt++;
          s[c].insert(x);
      }else if(opt&1){
          it=itt=s[c].upper_bound(x);--itt;--itt;
          update(rt,1,inf,x,-1,*itt);
          update(rt,1,inf,*it,*itt,x);
          s[c].erase(++itt);
          if(s[c].size()==2)cnt--;
      }else ans[c]=cnt<K?-1:query(x);
  }
  for(int i=1;i<=q;++i)printf("%d\n",ans[i]);
  return 0;
}

转载于:https://www.cnblogs.com/Paul-Guderian/p/10793238.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值