【APIO2018】新家(線段樹)
阿新 • • 發佈:2018-12-31
【APIO2018】新家(線段樹)
題面
題解
論比賽時想不到二分的危害,就只能Cu滾粗
既然不要線上,那麼考慮離線做法。
既然時間是區間,那麼顯然按照時間順序處理答案。
顯然答案具有可二分性,那麼對於當前位置而言,我們唯一要確定的就是\([x-mid,x+mid]\)區間內是否所有種類的商店都至少出現過了一次。
因為離線之後按照時間處理,因此已經沒有了時間這一維,只剩下了位置和種類兩個東西。那麼問題就是二維數點,但是因為要求每個種類只數一次,所以似乎沒法直接上樹套樹。
既然要維護這個東西,顯然需要額外記錄當前顏色的前驅位置,假裝為\(pre_i\)。
發現這樣子仍然需要依次考慮每一個顏色,那麼我們換一種方法。\(pre_i\)
那麼考慮如何維護\(pre_i\)的值就好了。
一個顯而易見的想法就是對於每個種類開一個\(set\)維護其出現的所有位置。首先維護一下無解的情況,然後因為可能一種顏色並沒有在\([x+mid+1,inf)\)範圍內出現,所以再額外維護一下每種顏色最後一個位置的最小值就好了。然而實際寫的時候並不需要維護最小位置,可以強行在\(inf\)
最後的二分可以選擇線上段樹上二分,二分右端點最終直接減一下就是答案了。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<cmath> #include<algorithm> #include<vector> #include<set> using namespace std; #define ll long long #define MAX 300300 #define inf 1000000000 inline int read() { int x=0;bool t=false;char ch=getchar(); while((ch<'0'||ch>'9')&&ch!='-')ch=getchar(); if(ch=='-')t=true,ch=getchar(); while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar(); return t?-x:x; } int n,K,Q,Typecnt,ans[MAX]; struct Work { int y;//time int x,t;//position,type int opt;//add/del/query }p[MAX*3]; int tot; bool operator<(Work a,Work b){if(a.y!=b.y)return a.y<b.y;return a.opt<b.opt;} multiset<int>::iterator it,itt; multiset<int> S[MAX],tt[MAX*20]; struct Node{int mn,ls,rs;}t[MAX*20]; int TOT,rt; void Modify(int &x,int l,int r,int p,int Add,int Del) { if(!x)x=++TOT; if(l==r) { if(Add)tt[x].insert(Add); if(Del)tt[x].erase(tt[x].find(Del)); t[x].mn=(tt[x].empty()?inf:*tt[x].begin()); return; } int mid=(l+r)>>1; if(p<=mid)Modify(t[x].ls,l,mid,p,Add,Del); else Modify(t[x].rs,mid+1,r,p,Add,Del); t[x].mn=min(t[t[x].ls].mn,t[t[x].rs].mn); } int Query(int p) { int l=0,r=inf,x=rt,mn=inf; while(l<r) { int mid=(l+r)>>1,pos=min(t[t[x].rs].mn,mn); if(p>mid||(mid-p<p-pos))l=mid+1,x=t[x].rs; else r=mid,x=t[x].ls,mn=pos; } return l-p; } int main() { n=read();K=read();Q=read();t[0].mn=inf; for(int i=1;i<=n;++i) { int x=read(),t=read(),a=read(),b=read(); p[++tot]=(Work){a,x,t,1}; p[++tot]=(Work){b+1,x,t,-1}; } for(int i=1;i<=Q;++i) { int x=read(),y=read(); p[++tot]=(Work){y,x,i,2}; } sort(&p[1],&p[tot+1]); for(int i=1;i<=K;++i)S[i].insert(inf),S[i].insert(-inf); for(int i=1;i<=K;++i)Modify(rt,0,inf,inf,-inf,0); for(int i=1;i<=tot;++i) { int opt=p[i].opt; if(opt==1)//Add { it=itt=S[p[i].t].upper_bound(p[i].x); --itt; Modify(rt,0,inf,*it,p[i].x,*itt); Modify(rt,0,inf,p[i].x,*itt,0); if(S[p[i].t].size()==2)++Typecnt; S[p[i].t].insert(p[i].x); } else if(opt==-1)//Del { it=itt=S[p[i].t].upper_bound(p[i].x); --itt;--itt; Modify(rt,0,inf,*it,*itt,p[i].x); Modify(rt,0,inf,p[i].x,0,*itt); S[p[i].t].erase(S[p[i].t].find(p[i].x)); if(S[p[i].t].size()==2)--Typecnt; } else ans[p[i].t]=Typecnt<K?-1:Query(p[i].x); } for(int i=1;i<=Q;++i)printf("%d\n",ans[i]); return 0; }