【洛谷4097】[HEOI2013] Segment(初學李超線段樹)
阿新 • • 發佈:2021-07-10
有$q$次操作,分為兩種:加入一條線段,詢問與直線$x=k$交點縱座標最大的線段的最小編號。
- 有\(q\)次操作,分為兩種:加入一條線段,詢問與直線\(x=k\)交點縱座標最大的線段的最小編號。
- \(q\le10^5,x\le39989\),強制線上
李超線段樹
第一次寫李超線段樹,之前遇到過每次插入直線的題目是直接靠線段樹上二分過的。。。
其實思想也挺簡單的,就是考慮線上段樹的每個節點上記錄一條線段。
當我們插入一條線段的時候,先找出它的橫座標區間線上段樹上對應的點,然後給這些點對應的區間分別執行插入操作。
如果當前點原本沒有記錄線段,那麼就直接記下插入的線段。
否則,我們比較插入線段與記錄線段在\(x=mid\)處的取值,取較優的那條為記錄線段,較劣的那條為新的插入線段。
然後分別判斷插入線段在左右兩區間是否被完全碾壓,如果沒有被完全碾壓就進去繼續執行插入操作。
由於不可能在兩個區間都沒有被完全碾壓,因此每次至多進入一個子區間,複雜度就有了保障。
詢問時求出根節點到詢問點一路上最優的線段即可。
程式碼:\(O(nlog^2n)\)
敗得義無反顧,弱得一無是處#include<bits/stdc++.h> #define Tp template<typename Ty> #define Ts template<typename Ty,typename... Ar> #define Rg register #define RI Rg int #define Cn const #define CI Cn int& #define I inline #define W while #define N 100000 #define X 39989 #define Y 1000000000 #define DB double #define eps 1e-9 using namespace std; DB k[N+5],b[N+5]; namespace FastIO { #define FS 100000 #define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++) #define pc(c) (FC==FE&&(clear(),0),*FC++=c) int OT;char oc,FI[FS],FO[FS],OS[FS],*FA=FI,*FB=FI,*FC=FO,*FE=FO+FS; I void clear() {fwrite(FO,1,FC-FO,stdout),FC=FO;} Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));} Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);} Tp I void writeln(Ty x) {W(OS[++OT]=x%10+48,x/=10);W(OT) pc(OS[OT--]);pc('\n');} }using namespace FastIO; class SegmentTree { private: #define PT CI l=1,CI r=X,CI rt=1 #define LT l,mid,rt<<1 #define RT mid+1,r,rt<<1|1 #define F(id,x) (k[id]*x+b[id]) #define Cmp(x,A,B) (fabs(F(A,x)-F(B,x))<eps?A<B:F(A,x)>F(B,x)) int P[N<<2]; I void T(RI id,PT)//插入線段 { if(!P[rt]) return (void)(P[rt]=id);RI mid=l+r>>1;//原本沒有線段則直接記錄 Cmp(mid,id,P[rt])&&(swap(P[rt],id),0);if(l==r) return;//記錄較優的,插入較劣的 Cmp(l,id,P[rt])&&(T(id,LT),0),Cmp(r,id,P[rt])&&(T(id,RT),0);//進入未被完全碾壓的子區間 } public: I void U(CI L,CI R,CI id,PT)//找到橫座標區間對應節點插入 { if(L<=l&&r<=R) return T(id,l,r,rt);RI mid=l+r>>1; L<=mid&&(U(L,R,id,LT),0),R>mid&&(U(L,R,id,RT),0); } I int Q(CI x,PT)//詢問,求出一路上最優的線段 { if(l==r) return P[rt];RI mid=l+r>>1,t=x<=mid?Q(x,LT):Q(x,RT); return P[rt]&&t?(Cmp(x,P[rt],t)?P[rt]:t):P[rt]|t; } }S; int main() { RI Qt,op,x,y,xx,yy,ct=0,lst=0;read(Qt);W(Qt--) { if(read(op),!op) {read(x),writeln(lst=S.Q((x+lst-1)%X+1));continue;}//詢問 read(x,y,xx,yy),x=(x+lst-1)%X+1,y=(y+lst-1)%Y+1,xx=(xx+lst-1)%X+1,yy=(yy+lst-1)%Y+1; if(++ct,x==xx) {k[ct]=1,b[ct]=max(y,yy)-x,S.U(x,x,ct);continue;}//特判與x軸垂直,只取最上方一個點 x>xx&&(swap(x,xx),swap(y,yy),0),k[ct]=1.0*(yy-y)/(xx-x),b[ct]=y-k[ct]*x,S.U(x,xx,ct);//求出解析式,然後插入 }return clear(),0; }