1. 程式人生 > 其它 >【洛谷4097】[HEOI2013] Segment(初學李超線段樹)

【洛谷4097】[HEOI2013] Segment(初學李超線段樹)

有$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;
}
敗得義無反顧,弱得一無是處