APIO2018 新家
阿新 • • 發佈:2018-11-05
目錄
APIO2018 新家
題意
五福街是一條筆直的道路,這條道路可以看成一個數軸,街上每個建築物的座標都可以用一個整數來表示。小明是一位時光旅行者,他知道在這條街上,在過去現在和未來共有 \(n\) 個商店出現。第 \(i\) 個商店可以使用四個整數 \(x_i\), \(t_i\), \(a_i\), \(b_i\)描述,它們分別表示:商店的座標、商店的型別、商店開業的年份、商店關閉的年份。
小明希望通過時光旅行,選擇一個合適的時間,住在五福街上的某個地方。他給出了一份他可能選擇的列表,上面包括了 \(q\)
現在,他想計算出在這些時間和地點居住的生活質量。他定義居住的不方便指數為:在居住的年份,離居住點最遠的商店型別到居住點的距離。型別 \(t\) 的商店到居住點的距離定義為:在指定的年份,型別 \(t\) 的所有營業的商店中,到居住點距離最近的一家到居住點的距離。我們說編號為 \(i\) 的商店在第 \(y\) 年在營業當且僅當 \(a_i \leq y \leq b_i\)。注意,在某些年份中,可能在五福街上並非所有 \(k\)
\((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\)
然後實際上這題還可以繼續優化,我們會發現我們在計算答案的時候,即用到了二分,又用到了線段樹。然而實際上,線段樹和二分往往是可以並在一起的(霧,然後我們就可以線上段樹上直接進行二分。原本二分的時候,我們如果即\([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;
}