1. 程式人生 > 其它 >關於李超線段樹和自己的感想

關於李超線段樹和自己的感想

李超線段樹用來維護:給你\(n\)條直線,求出當\(x=a\)時的最大值,支援線上插入.

具體實現:

  我們對於線段樹上的每一個節點,記錄在\(x=mid\)時的最優線段.

  此時我們即將要插入一條線段為\(A=kx+b\),我們計算一下它在中點處的值為\(val\),\(p\)表示我們記錄的線段.

  1. \(val>val_p\),我們比較一下斜率.

     如果\(k>k_{p}\),那麼右半區間的答案線段\(A\)一定更優,我們需要尋找一個區間使得\(p\)能成為最優線段.那麼我們遞迴左半區間.

     如果\(k\leq k_{p}\),那麼左半區間的答案線段\(A\)

一定最優,同理我們遞迴右半區間.

  2. \(val\leq val_p\).

     如果\(k_{p}> k\),那麼右半區間的答案線段\(p\)一定更優,我們需要尋找一個區間使得\(A\)能成為最優線段.那麼我們遞迴左半區間.

     如果\(k_{p}\leq k\),那麼左半區間的答案線段\(p\)一定最優,同理我們遞迴右半區間.

詢問\(x=a\)

  我們一路遞迴下來,對於每個區間的最優線段取一個最大值.

合併

  像線段樹合併一樣,我們將另一棵樹的最優線段插入到這棵樹內.

  合併時間和空間複雜度應該為\(O(n\log n)\).

  如果只插入那麼空間複雜度應該為\(O(n)\)

,時間複雜度為\(O(n\log n)\).

程式碼
struct node {
	int x;
	LL b;
}t[M*20];
struct SEG {
	int Ls[M*20],Rs[M*20];
	LL Calc(node a,LL K) {return a.x*K+a.b;}
	void Insert(int &p,int l,int r,node val) {
		if(!p) {
			p=++Tot;
			t[p]=val;
			return;
		}
		if(l==r) {
			if(Calc(val,C[l])>Calc(t[p],C[l]))t[p]=val;
			return;
		}
		int mid=l+r>>1;
		if(Calc(t[p],C[mid])<Calc(val,C[mid])) {
			if(t[p].x<val.x)Insert(Ls[p],l,mid,t[p]);
			else Insert(Rs[p],mid+1,r,t[p]);
			t[p]=val;
		}else {
			if(t[p].x<val.x)Insert(Rs[p],mid+1,r,val);
			else Insert(Ls[p],l,mid,val);
		}
	}
	LL query(int p,int l,int r,int pos) {
		if(!p)return 0LL;
		if(l==r)return Calc(t[p],C[pos]);
		int mid=l+r>>1;
		LL mx=Calc(t[p],C[pos]);
		if(pos<=mid)return max(mx,query(Ls[p],l,mid,pos));
		return max(mx,query(Rs[p],mid+1,r,pos));
	}
	int Merge(int rt,int lt,int l,int r) {
		if(!rt||!lt)return rt|lt;
		Insert(rt,l,r,t[lt]);
		int mid=l+r>>1;
		Ls[rt]=Merge(Ls[rt],Ls[lt],l,mid);
		Rs[rt]=Merge(Rs[rt],Rs[lt],mid+1,r);
		return rt;
	}
}T;
想法:

  考試時想出了一個寫法來替代李超線段樹,但是常數更大,記憶體更大,程式碼更長,還不好合並兩棵樹.起碼我沒有想到.

  因為一條線段取到最優值應該是一個連續的區間\([l,r]\).我大膽猜測如果將這個線段與其他線段的最優值做差,那麼這應該是一個有極值的函式.

  那麼我們可以用三分轉二分的方式,求解出一條線段的\([l,r]\),再將這個二分放線上段樹上我們就可以在\(n\log n\)的複雜度插入一個線段了.

程式碼:
void Down(int p) {
	if(tag[p]==-1)return;
	tag[ls]=tag[p];Ls[ls]=Rs[ls]=tag[p];
	tag[rs]=tag[p];Ls[rs]=Rs[rs]=tag[p];
	tag[p]=-1;
}
void Update(int p,int l,int r,int lx,int rx,int num) {
	if(l==lx&&r==rx) {
		tag[p]=num;
		Ls[p]=Rs[p]=num;
		return;
	}
	Down(p);
	int mid=l+r>>1;
	if(rx<=mid)Update(ls,l,mid,lx,rx,num);
	else if(lx>mid)Update(rs,mid+1,r,lx,rx,num);
	else Update(ls,l,mid,lx,mid,num),Update(rs,mid+1,r,mid+1,rx,num);
	Up(p);
}
LL Calc(int num,int now,int k) {
	return suma[num]-1LL*k*sumb[num]-(suma[now]-1LL*k*sumb[now]);
}
int Query(int p,int l,int r,int pos) {
	if(l==r)return tag[p];
	Down(p);
	int mid=l+r>>1;
	if(pos<=mid)return Query(ls,l,mid,pos);
	return Query(rs,mid+1,r,pos);
}
int Query_L(int p,int l,int r,int pos) {
	if(l==r) {
		if(Calc(pos,tag[p],l-Mx)>=0)return -1;
		return l;
	}
	Down(p);
	int mid=l+r>>1;
	LL lx=Calc(pos,Rs[ls],mid-Mx);
	LL rx=Calc(pos,Ls[rs],mid+1-Mx);
	if(lx<=rx||lx<0)return Query_L(ls,l,mid,pos);
	return Query_L(rs,mid+1,r,pos);
}
int Query_R(int p,int l,int r,int pos) {
	if(l==r)return l;
	Down(p);
	int mid=l+r>>1;
	LL lx=Calc(pos,Rs[ls],mid-Mx);
	LL rx=Calc(pos,Ls[rs],mid+1-Mx);
	if(rx<=lx||rx<0)return Query_R(rs,mid+1,r,pos);
	return Query_R(ls,l,mid,pos);
}
void Update_Mi(int now) {
	int resL=Query_L(1,0,Mxx,now);
	if(resL==-1)return;
	int resR=Query_R(1,0,Mxx,now);
	Update(1,0,Mxx,resL,resR,now);
}