一、题目
二、解法
不难发现答案具有单调性,所以对于每个询问都可以二分答案。
具体来说对于位置 x x x,如果答案 ≥ m \geq m ≥m,那么等价于存在一种店在 ( x − m , x + m ) (x-m,x+m) (x−m,x+m)不存在店,但是区间并不好维护,可以使用前驱来判断存在与否,那么等价于在 [ x + m , i n f ) [x+m,inf) [x+m,inf)存在店的前驱 ≤ x − m \leq x-m ≤x−m,那么我们只需要判断 [ x + m , i n f ) [x+m,inf) [x+m,inf)前驱的最小值 m n ≤ x − m mn\leq x-m mn≤x−m
每个位置的前驱,用线段树来维护即可,一开始可以把所有用到的位置离散化,然后对于每种店都维护一个 s e t \tt set set来算前驱,先要插入 − i n f -inf −inf和 i n f inf inf。每一个位置用 s e t \tt set set维护前驱(位置要先离散化),那么一开始位置 i n f inf inf的前驱就有 k k k个 − i n f -inf −inf,我们要有所有的店种才能去掉这个 − i n f -inf −inf,所以判断位置 i n f inf inf是否有 − i n f -inf −inf就可以知道是否所有店种齐备。
二分可以在线段树上实现,我们二分 x + m x+m x+m为 i i i,那么 m n + i ≤ 2 x mn+i\leq 2x mn+i≤2x,我们要明确 i i i越小那么 m n mn mn也越小,他们两个都是单调的。如果左边的最大位置 < x <x <x那么只能往右边分,否则我们判断 m i d + 1 mid+1 mid+1( m i d mid mid指中间的位置)能不能满足条件,如果满足我们往右边分;不满足往左边分,还要用右边的前驱更新 m n mn mn,到了最底层的位置也就知道答案了。
需要注意 m u l t i s e t \tt multiset multiset的使用方法,删除一个元素只能删指针,删值会把相等的所有值都删去。
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <set>
using namespace std;
#define point multiset<int>::iterator
const int M = 300005;
const int inf = 1e8+100;
int read()
{
int num=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=(num<<3)+(num<<1)+(c^48),c=getchar();
return num*flag;
}
int n,m,N,k,d,a[M],ans[M],mn[4*M],mx[4*M];
struct node
{
int x,t,a;
bool operator < (const node &b)
{
return a<b.a;
}
}qa[M],qb[M],q[M];
multiset<int> s[M],p[M];
void lisan()
{
sort(a+1,a+1+n);
n=unique(a+1,a+1+n)-a;//n+1¸öµã
for(d=1;d<n;d*=2);d--;
for(int i=1;i<=1200000;i++) mn[i]=inf;
for(int i=1;i<=k;i++) p[n].insert(-inf);
for(int i=1;i<=n;i++) p[i].insert(inf);
for(int i=d+n;i;i>>=1) mn[i]=-inf;
for(int i=1;i<n;i++) mx[i+d]=a[i];
mx[n+d]=2*inf;
for(int i=d;i>=1;i--)
mx[i]=max(mx[i<<1],mx[i<<1|1]);
}
void upd(int i)
{
mn[i+d]=(*p[i].begin());
i+=d;
while(i>>=1)
{
mn[i]=min(mn[i<<1],mn[i<<1|1]);
}
}
int ask(int x)
{
if(mn[n+d]==-inf) return -1;
int i=1,mi=inf;
while(i<=d)
{
if(mx[2*i]<x) {i=2*i+1;continue;}
if(mx[2*i]+1+min(mi,mn[2*i+1])<=2*x) i=2*i+1;
else
{
mi=min(mi,mn[2*i+1]);
i=2*i;
}
}
return min(x-min(mi,mn[i]),mx[i]-x);
}
void Add(int i,int x)
{
p[i].insert(x);
upd(i);
}
void Del(int i,int x)
{
point it=p[i].find(x);
p[i].erase(it);
upd(i);
}
int get(int x)
{
return lower_bound(a+1,a+n,x)-a;
}
void add(int i,int x)
{
point it1=s[i].insert(x),it2=it1;
it1--;it2++;
Add(get(x),*it1);
Del(get(*it2),*it1);Add(get(*it2),x);
}
void del(int i,int x)
{
point it=s[i].find(x),it1=it,it2=it1;
it1--;it2++;
Del(get(x),*it1);
Del(get(*it2),x);Add(get(*it2),*it1);
s[i].erase(it);//multiset can only delete the point! Attention!
}
signed main()
{
N=n=read();k=read();m=read();
for(int i=1;i<=n;i++)
{
int x=read(),t=read(),l=read(),r=read();
qa[i]=node{x,t,l};
qb[i]=node{x,t,r};
a[i]=x;
}
lisan();
for(int i=1;i<=m;i++)
{
int l=read(),y=read();
q[i]=node{l,i,y};
}
sort(qa+1,qa+1+N);
sort(qb+1,qb+1+N);
sort(q+1,q+1+m);
for(int i=1;i<=k;i++)
s[i].insert(inf),s[i].insert(-inf);
for(int i=1,ja=1,jb=1;i<=m;i++)
{
while(ja<=N && qa[ja].a<=q[i].a) add(qa[ja].t,qa[ja].x),ja++;
while(jb<=N && qb[jb].a<q[i].a) del(qb[jb].t,qb[jb].x),jb++;
ans[q[i].t]=ask(q[i].x);
}
for(int i=1;i<=m;i++)
printf("%d\n",ans[i]);
}