關於李超線段樹和自己的感想
阿新 • • 發佈:2021-11-14
李超線段樹用來維護:給你\(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)\)
程式碼
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);
}