李超线段树

介绍

李超线段树是一个特别神奇的东西。TA可以把几何和线段树完美的融合起来。

题目中如果说给你一些一次函数(形如\(kx+b\)),也就是直线,或者给你一条线段(四个端点的坐标)就可以考虑用一下。

例题:洛谷P4254

题目概括:给你一些直线,同一个\(x\)坐标下\(y\)坐标的最大值。\(\color{FFFFFF}\tiny\text{众所周知,一条直线如果k>0,那TA就是单调递增的,如果k<0则是单调递减的。如果k=0,那TA平行于x轴}\)

考虑什么时候会改变最大值。

我们李超线段树存的是当前\(mid\)最大值。当\(l=r\)的时候就能查询到点了

现在新插了一条直线。因为新插直线\(mid\)>以前所有\(mid\),所以肯定要把TA设为最优。但是\(l\)\(r\)又不一定最优,所以有人就巧妙的想出来一个方法想你打不过逃课一样

既然新线段比旧线段更好,那就让旧线段更新新线段。这就像你打瓦攻防转换一样。

为啥可以那么做,很显然。你的新线段已经是最优的了,那旧线段就不是最优的了。那肯定再更旧线段就没意义了。

所以你一个

交换一下就行了。

然后再比较\(l\)\(r\)。因为你\(mid\)更优不代表你的\(l\)\(r\)更优。

然后发现\(r\)比较大,然后就去更新左边

果然,现在新线段又比老线段好了。那再一个

换一下,然后一直比较就行。

时间复杂度证明

因为这两条线段时单调的,所以TA俩要么更新左边要么更新右边。那就是每次区间大小缩小\(\frac{1}{2}\),所以复杂度是\(O(n\log_2n)\)

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef long double lb;
const int N=5e4+10;
const int Q=1e5+10;
lb k[Q],b[Q];
ll tree[N<<2];
ll n=0;
ll ls(ll p){
	return p<<1;
}
ll rs(ll p){
	return p<<1|1;
}
lb ans(ll p,ll x)//算出函数值
{
	return k[p]*x+b[p];
}
void update(ll p,ll l,ll r,ll x)
{
	ll mid=l+r>>1;
	if(ans(x,mid)>ans(tree[p],mid))swap(tree[p],x);//与此同时,在另一边
	if(ans(x,l)>ans(tree[p],l))update(ls(p),l,mid,x);//左边大更左边,右边大更右边
	if(ans(x,r)>ans(tree[p],r))update(rs(p),mid+1,r,x);
    //根据一次函数单调性可知最多有有一个交点,保证了复杂度
}
lb query(ll p,ll l,ll r,ll x)//查询x的值
{
	lb res=ans(tree[p],x);
	if(l==r)return res;
	ll mid=l+r>>1;
	if(x<=mid)return max(res,query(ls(p),l,mid,x));
	else return max(res,query(rs(p),mid+1,r,x));
}
char s[8];
int main()
{
	ll q;
	scanf("%lld",&q);
	while(q--)
	{
		
		scanf("%s",&s);
		if(s[0]=='P')
		{
			n++;
			scanf("%Lf%Lf",&b[n],&k[n]);
			b[n]-=k[n]; //这里第一天没有+k,所以要减去
			update(1,1,N,n);
		 } 
		else
		{
			ll x;
			scanf("%lld",&x);
			printf("%lld\n",max(0ll,(ll)(query(1,1,N,x))/100));
		}
		
	}
	return 0;
}

这里样例太水了。所以给出一个新样例
来源

14
23
Project 2.31 14.71
Query 15
Query 12
Project 2.63 0.41
Query 15
Query 10
Project -24.94 12.65
Project -13.85 20.44
Query 9
Query 13
Query 7
Query 11
Project 0.22 3.04
Project -18.09 11.54
Project -1.91 22.95
Query 14
Project -15.65 22.67
Project -24.78 16.07
Project -22.23 1.33
Project 7.66 8.59
Query 4
Query 10
Query 7
2
1
2
1
1
2
1
1
2
0
2
1

注意\(k\)\(b\)不要输反了。先输入b$$再输入\(k\)

posted @ 2026-01-20 17:23  NumLuck  阅读(6)  评论(0)    收藏  举报