首页 > 其他分享 >李超线段树

李超线段树

时间:2024-07-13 13:41:42浏览次数:12  
标签:int 线段 李超 mid 区间 最优 now

李超线段树

李超线段树

发现要维护的问题十分难做,所以我们要引入李超线段树。

我们发现,如果在一个区间内,一条线段的整体在另一条线段上方,那么这条线段一定更优,我们称之为最优线段。但是如果并不是这样,应该如何做呢?

这里给出线段为一个区间内的最优线段的条件:

  • 线段的定义域覆盖了整个区间。

  • 线段在区间中点的位置最高。

现在我们假设 \(l_1\) 为区间 \([l,r]\) 内的最优线段,我们要插入一条新的线段 \(l_2\)。

如果当前区间没有最优线段,或者 \(l_2\) 整体在 \(l_1\) 上方,那么可以直接用 \(l_2\) 替换 \(l_1\) 作为该区间的最优线段。

而如果 \(l_1\) 整体在 \(l_2\) 上方,那么显然 \(l_2\) 是劣的,不用再进行修改及递归。

最后是比较难的一种情况:如果两个线段谁也没有被谁完全压住,那么他们一定出现交点。

下面记区间中点为 \(mid\)。

我们比较两条线段在 \(mid\) 处的高度,更高的那条作为最优线段。但是,虽然剩下那一条线段在这个区间不是最优的,但是可能在子区间内最优,所以我们要进行递归,用更劣的那条线段更新子区间。

我们记 \(l_3\) 为更新子区间的线段(其实就是上面两条线段更劣的那一个)。我们对交点的位置进行分类讨论:

  • 交点在 \(mid\) 左侧:画个图可以发现 \(l_3\) 只可能在左子区间成为最优线段,故递归左子区间。

  • 交点在 \(mid\) 右侧:同理,递归右子区间。

  • 交点在 \(mid\) 处:看 \(l_3\) 在 \(l\) 还是 \(r\) 的位置更高,就选择哪一边递归。

我们的基本思想就说完了,下面说一下作者在写这道题的时候遇见的错误:

  • 把所有模数都看成 \(39989\)。

  • 没有看到交点相同时要选择编号最小的线段。

既然是板子题,下面就放一下代码:

#include<bits/stdc++.h>
#define int long long
#define N 100005
#define mod1 39989
#define mod2 1000000000
#define eps 1e-8
using namespace std;
int cnt,res,tot,rt;
struct line{
	double k,b;
}a[N];
struct node{
	int ls,rs,id;
}w[N<<2];
bool check(int i,int j,int x){
	if(a[i].k*x+a[i].b-a[j].k*x-a[j].b>eps)return 1;
	if(a[j].k*x+a[j].b-a[i].k*x-a[i].b>eps)return 0;
	return i<j;//函数用于判断哪个线段更优 
}
void modify(int &u,int l,int r,int L,int R,int now){
	if(l>R||r<L)return;//完全无交 
	if(!u)u=++cnt;//动态开点 
	if(l>=L&&r<=R){//完全包含 
		if(check(now,w[u].id,l)&&check(now,w[u].id,r)){//新的线段更优,直接更新 
			w[u].id=now;
			return;
		}
		if(check(w[u].id,now,l)&&check(w[u].id,now,r)){//旧的线段更优,直接跳过 
			return;
		}
		int mid=l+r>>1;
		if(check(now,w[u].id,mid)){
			swap(now,w[u].id);//区间内最优线段 
		}
		if(check(now,w[u].id,l)){//用更劣的线段更新子区间 
			modify(w[u].ls,l,mid,L,R,now);
		}
		if(check(now,w[u].id,r)){
			modify(w[u].rs,mid+1,r,L,R,now);
		}
	}
	else{
		int mid=l+r>>1;
		modify(w[u].ls,l,mid,L,R,now);//不被完全包含,递归解决 
		modify(w[u].rs,mid+1,r,L,R,now);
	}
}
void solve(int u,int l,int r,int p){
	if(!u||r<p||l>p)return;//没有点或者完全无交 
	if(check(w[u].id,res,p)){//当前答案比之前的答案更优 
		res=w[u].id;
	}
	if(l==r)return;
	int mid=l+r>>1;
	solve(w[u].ls,l,mid,p);//递归解决 
	solve(w[u].rs,mid+1,r,p);
}
signed main(){
	int n,las=0;
	cin>>n;
	while(n--) {
		int op,x_0,y_0,x_1,y_1;
		cin>>op;
		if(op==0){
			int k;
			cin>>k;
			res=0;
			k=(k+las-1)%mod1+1;
			solve(1,1,mod1,k);//查询答案 
			las=res;
			cout<<res<<'\n';
		}
		else{
			cin>>x_0>>y_0>>x_1>>y_1;
			x_0=(x_0+las-1)%mod1+1;
			y_0=(y_0+las-1)%mod2+1;
			x_1=(x_1+las-1)%mod1+1;
			y_1=(y_1+las-1)%mod2+1;
			if(x_0>x_1)swap(x_0,x_1),swap(y_0,y_1);//发现反了要换回来 
			if(x_0==x_1)a[++tot]={0,max(y_0,y_1)*1.0};
			else a[++tot].k=1.0*(y_1-y_0)/(x_1-x_0),a[tot].b=y_0-x_0*a[tot].k;//求斜率和截距 
			modify(rt,1,mod1,x_0,x_1,tot);//修改 
		}
	}
	return 0;
}

标签:int,线段,李超,mid,区间,最优,now
From: https://www.cnblogs.com/zxh923aoao/p/18299978

相关文章

  • 中位数(权值线段树版)
    中位数题目描述给定一个长度为NNN的非负整数序列AAA,对于前奇数......
  • 线段树分治学习笔记
    线段树分治是一种通过线段树维护时间轴,实现一些可撤销的信息维护问题的手段。线段树分治是离线算法。具体地,对于若干个修改与询问,按照时间戳像区间修改一样挂在线段树的节点上,然后遍历整棵线段树,将节点上的操作计入贡献,对于一个时间戳为\(t\)的询问,线段树上区间\([t,t]\)即......
  • CF1864F Exotic Queries (离线+线段树+树状数组)
    CF1864FExoticQueries离线+线段树+树状数组先把权值在\([l,r]\)之内的单独拎出来看性质。可以知道策略一定是元素从小到大消成\(0\)。当消除元素\(x\)时,最好的情况当然是一次全消了,但一般元素\(x\)的位置两两之间会有之前消成的\(0\),将所有位置分成了\(n\)段,那么消......
  • openlayer, 由一个图标遮盖线段需求引发的思考
    最近碰到这么一个需求:有一些面(Polygon)和线(LineString),需要在面的边线上或者在线上均匀绘制一些矩形图标(在各种分辨率下)。在一个Polygon的边线或者LineString上绘制一个矩形图标时,图标会遮住线,如果图标是部分镂空的,就会看到线从图标中穿过,如何让图标看起来遮住了线呢,自然而然就会......
  • GLFLS课程:线段树进阶
    线段树合并与分裂线段树合并两个权值线段树\(T_1\)和\(T_2\)的合并是一个递归的过程。我们不妨设要合并的子树为\(x\)和\(y\),其对应区间均为\([l,\r]\)那么分类讨论如下:  \((1)\)首先若\(x=0\)或\(y=0\),则\(x\),\(y\)中有空节点,直接返回\(x+y\)即可......
  • 20240706总结(线段树应用)
    A-PhysicalEducationLessonsCF915EPhysicalEducationLessons题解:没什么好说的,动态开点模板题(好像普通线段树也可以做)B-GCDofanArrayCF1493DGCDofanArray题解:暴力分解质因数,修改的时候也把x分解,对每个质数开一个可重集合(multiset)记录一下每个质数出现的不同位......
  • 李超线段树 学习笔记
    问题引入:在一个平面内,有\(n\)次操作,这些操作分为\(2\)种:第一种是在平面内加入一条线段;第二种是给定一个\(k\),查询直线\(x=k\)与这些线段交点的最大值。(强制在线,\(n\le10^5,1\lex\le39989,-10^9\ley\le10^9\))求这种用区间覆盖的问题,一般我们会想到线段树。但是一般......
  • [学习笔记] 动态开点权值线段树合并 - 数据结构
    权值线段树例题【模板】普通平衡树#include<bits/stdc++.h>usingnamespacestd;constintN=1e5+1;intn,val[N],opt[N],num[N],cnt,len,san[N],m[N],rnk[N];unordered_map<int,int>dfn;structWeightedSegmentTree{ #definels(id<<1) #define......
  • 线段树的基本知识和初级运用
    前言线段树绝对是出题人最爱考的高级数据结构了。它快、灵活、码量也大,相当考验OIer的综合能力。所以好好学习一下线段树是相当必要的。基础线段树是基于二叉树的。通过为二叉树的每个节点赋予线段的意义,线段树可以维护很多的区间信息,包括但不限于区间和、区间最大值、区间第......
  • [Hackerrank University Codesprint 5] Sword profit (李超线段树)
    [HackerrankUniversityCodesprint5]Swordprofit李超线段树考虑大力推式子。写出在第\(i\)所商店的第\(k\)把剑在第\(j\)所商店卖掉的价格。\[\text{profit}=\max(0,q_i-(j-i)\cdotd_i-r_j)-(a_i+k\cdotb_i)\]显然利益一定要是正的才有价值,所以\(\max\)可以改到......