码迷,mamicode.com
首页 > 其他好文 > 详细

「李超线段树」

时间:2021-04-24 11:51:10      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:pre   函数   标记   block   cst   span   class   strong   alc   

省选之前的知识了,现在省选苟进队后赶紧补一下

李超线段树是由李超发明的用于求函数定点最值线段树,又名李超树

例题

[HEOI2013]Segment

大意是,在一个二维平面上,依次加入若干条线段,询问对于某个 \(x\) 的最大值,强制在线

李超树像普通线段树一样同样支持两种操作:插入和查询

插入

在李超树上每个节点 \([l,r]\),存了一个优势线段,这条线段在 \(mid\) 的值是最大的

考虑在区间 \([s,t]\) 插入一条线段 \(l_0\),每到一个线段树上的节点,与节点上存的优势线段 \(l_1\) 比较

  • 如果 \(l_0\)\(x=mid\) 上的值比 \(l_1\) 大,显然这个节点的优势线段变成了 \(l_0\)\(swap\;l_0\)\(l_1\)

  • 如果 \(l_0\)\(x=l\) 上的值比 \(l_1\) 大,要么 \(l_0\)\([l,mid]\) 这段区间覆盖了 \(l_1\),或者 \(l_0\)\(l_1\)\([l,mid]\) 中有交点,递归到左区间

  • 如果 \(l_0\)\(x=r\) 上的值比 \(l_1\) 大,要么 \(l_0\)\([mid + 1,r]\) 这段区间覆盖了 \(l_1\),或者 \(l_0\)\(l_1\)\([mid+1,r]\) 中有交点,递归到右区间

\(swap\;l_0\)\(l_1\) 是因为,如果 \(l_0\) 成为了这个区间的优势线段,那么 \(l_1\) 也有可能与 \(l_0\) 从而递归到儿子区间

因为如果 \(l_0\)\(l_1\) 有交,只会有一个交点,所以只会递归进一个儿子,单次插入复杂度是 \(\Theta (\log n)\)

查询

查询对于某个 \(x=k\) 的极值,在李超树上向下递归,用所有包含它的线段树区间的优势线段求出若干个值,一块取个 \(\max\) 就是答案

考虑为什么正确,首先我们插入一条线段时,当且仅当当前的优势线段完全覆盖了这个条线段,才会把这个线段舍去,所以不会影响答案的正确性

然后考虑为什么把路径上的所有值都求一遍,因为这个节点存的线段可能与儿子节点的线段有交点,到底取哪个线段作为最大值并不确定,所以把路径上的都求一遍取个最大值,跟标记永久化差不多

然后这个题就很好解决了,直接套用板子就行了

板子
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

const int maxn = 1e5 + 50, INF = 0x3f3f3f3f;
const double EPS = 1e-8;

inline int read () {
	register int x = 0, w = 1;
	register char ch = getchar ();
	for (; ch < ‘0‘ || ch > ‘9‘; ch = getchar ()) if (ch == ‘-‘) w = -1;
	for (; ch >= ‘0‘ && ch <= ‘9‘; ch = getchar ()) x = x * 10 + ch - ‘0‘;
	return x * w;
}

int T, n, m = 4e4, lastans;
int tree[maxn << 2];

struct Line {
	double k, b;
	Line () {}
	Line (register double x, register double y) { k = x, b = y; }
} a[maxn];

inline double Calc (register Line a, register int x) {
	return a.k * x + a.b;
}

inline void Modify (register int rt, register int l, register int r, register int s, register int t, register int x) { // 插入一条线段
	register int mid = (l + r) >> 1;
	if (s <= l && r <= t) {
		if (Calc (a[x], mid) - Calc (a[tree[rt]], mid) > EPS) swap (tree[rt], x);
		if (Calc (a[x], l) - Calc (a[tree[rt]], l) > EPS) Modify (rt << 1, l, mid, s, t, x);
		if (Calc (a[x], r) - Calc (a[tree[rt]], r) > EPS) Modify (rt << 1 | 1, mid + 1, r, s, t, x);
		return;
	}
	if (s <= mid) Modify (rt << 1, l, mid, s, t, x);
	if (t > mid) Modify (rt << 1 | 1, mid + 1, r, s, t, x);
}

inline int Query (register int rt, register int l, register int r, register int x) { // 单点查询
	if (l == r) return tree[rt];
	register int mid = (l + r) >> 1;
	if (x <= mid) {
		register int tmp = Query (rt << 1, l, mid, x);
		return Calc (a[tmp], x) - Calc (a[tree[rt]], x) > EPS ? tmp : tree[rt];
	} else {
		register int tmp = Query (rt << 1 | 1, mid + 1, r, x);
		return Calc (a[tmp], x) - Calc (a[tree[rt]], x) > EPS ? tmp : tree[rt];
	}
}

int main () {
	T = read();
	while (T --) {
		register int opt = read();
		if (! opt) {
			register int x = (read() + lastans - 1) % 39989 + 1;
			printf ("%d\n", lastans = Query (1, 1, m, x));
		} else {
			register int x0 = (read() + lastans - 1) % 39989 + 1, y0 = (read() + lastans - 1) % 1000000000 + 1;
			register int x1 = (read() + lastans - 1) % 39989 + 1, y1 = (read() + lastans - 1) % 1000000000 + 1;
			if (x0 > x1) swap (x0, x1), swap (y0, y1);
			if (x0 == x1) a[++ n] = Line (0, max (y0, y1));
			else a[++ n] = Line (1.0 * (y1 - y0) / (x1 - x0), y0 - 1.0 * (y1 - y0) / (x1 - x0) * x0);
			Modify (1, 1, m, x0, x1, n);
		}
	}
	return 0;
}

「李超线段树」

标签:pre   函数   标记   block   cst   span   class   strong   alc   

原文地址:https://www.cnblogs.com/Rubyonly233/p/14693361.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!