知识点 - 李超树
解决问题类型:
李超树是一种线段树的应用,主要应用场合为:单点集合域,区间等差插入,求单点最值。
- 动态地插入多条二维线段,询问某个 x x x对应的 y y y值的最值。
- 修改 [ L , R , b , k ] [L,R,b,k] [L,R,b,k],对于第 i ∈ [ L , R ] i∈[L,R] i∈[L,R]个集合,插入 b + ( i − L ) k b+(i−L)k b+(i−L)k,询问某个集合的最大值。
实现
-
每个线段树结点记录的是 x = m i d x = mid x=mid 时的最值线段
-
先考虑怎么查询。因为只能记录 x = m i d x = mid x=mid 时的最值线段,所有包含pos的大区间的答案可能不是最优解。所以要递归的往下查找,直到 L = R L=R L=R
-
考虑插入一条线段。若区间无线段,则成为最优线段。否则进行比较。
-
设 O l d Old Old为原线段在 l l l上的值, O l d Old Old_为在 r r r上的值。 N e w New New 同理。
-
简单的情况, O l d > N e w Old >New Old>New, O l d _ > N e w _ Old\_ > New\_ Old_>New_或相反时,显然可以直接判断孰优孰劣。
-
考虑存在交点的情况。
- 右图的情况下,交点小于一半,根据两条线的位置关系可以判断,右区间还是原来的线段更优。所以这种情况下,只需要递归的做左边即可。
- 考虑左图的情况,此时当前区间的最值在 n e w new new上,再结合我们的查询,我们还要记录 o l d old old(再右半边存在 o l d old old优于 n e w new new的情况)。所以将 o l d old old在右区间往下传递。
- 位置相反的时候也这样考虑一下就行。
复杂度
- 线段定义域固定时,查询和插入的时间复杂度都是 O ( l o g n ) O(logn) O(logn)
- 线段定义域不固定时,因为修改区间可能分为 l o g log log个区间,每个区间可能递归到底,所以插入的时间复杂度为 O ( l o g 2 n ) O(log^2n) O(log2n)查询复杂度为 O ( l o g n ) O(logn) O(logn)
- 查询为 O ( l o g n ) O(logn) O(logn)可以直接使用不固定的代码,时间复杂度该多少就是多少 ,但是它长啊 。
例题
代码
#include<bits/stdc++.h>
using namespace std;
#define LL long long
#define rep(i,a,b) for(int i=(int)(a);i<=(int)(b);i++)
#define per(i,a,b) for(int i=(int)(a);i>=(int)(b);i--)
#define mmm(a,b) memset(a,b,sizeof(a))
#define pb push_back
#define pill pair<int, int>
#define fi first
#define se second
#define debug(str) cerr<<#str<<" = "<<str<<'\n';
const LL mod=1e9+7;
const int maxn=5e4+9;
LL rd(){ LL ans=0; char last=' ',ch=getchar();
while(!(ch>='0' && ch<='9'))last=ch,ch=getchar();
while(ch>='0' && ch<='9')ans=ans*10+ch-'0',ch=getchar();
if(last=='-')ans=-ans; return ans;
}
/*_________________________________________________________head*/
struct line{
bool have;
double k,b;
}tr[maxn<<2];
#define ls (rt<<1)
#define rs (rt<<1|1)
#define mid (l+r>>1)
void update(double k,double b,int rt=1,int l=1,int r=5e4){ // insert y=kx+b
if(!tr[rt].have){
tr[rt].k=k,
tr[rt].b=b,
tr[rt].have=1;
return;
}
double Old=tr[rt].k*l+tr[rt].b,Old_=tr[rt].k*r+tr[rt].b;
double New=k*l+b,New_=k*r+b;
if(Old>=New&&Old_>=New_)return;
if(Old<=New&&Old_<=New_){
tr[rt].k=k,
tr[rt].b=b;
return;
}
// k won't equiv tr[rt].k
double pos=-(b-tr[rt].b)/(k-tr[rt].k);
if(Old<=New){
if(pos<=mid){
update(k,b,ls,l,mid);
}
else{
update(tr[rt].k,tr[rt].b,rs,mid+1,r);
tr[rt].k=k,tr[rt].b=b;
}
}
else{
if(pos>=mid){
update(k,b,rs,mid+1,r);
}
else{
update(tr[rt].k,tr[rt].b,ls,l,mid);
tr[rt].k=k,tr[rt].b=b;
}
}
}
double query(int x,int rt=1,int l=1,int r=5e4){ // query most value at x
double ans=-2e18;
if(tr[rt].have)
ans=tr[rt].k*x+tr[rt].b;
if(l==r)return ans;
if(x<=mid)ans=max(ans,query(x,ls,l,mid));
else ans=max(ans,query(x,rs,mid+1,r));
return ans;
}
char str[50];
int main(){
int t=rd();
while(t--){
scanf("%s",str);
if(str[0]=='P'){
double k,b;
scanf("%lf%lf",&b,&k);
update(k,b-k);
}
else{
int x;scanf("%d",&x);
printf("%d\n",max(0,(int)floor(query(x)/100.0)));
}
}
return 0;
}