李超线段树

用途:

用于二维坐标系维护多条线段。

算法:

本质上是采用标记永久化,
对每个线段树节点维护一个标记表示该区间存在这一条线段,
查询时从上到下经过节点的标记即为该横坐标上可能经过的线段。
下面需在标记(线段)间的比较上作考虑:建议画图理解
此时对于一个区间\([l, r]\)
找出中点\(mid\),以两线段(新线段,原标记)在中点大小关系分:下的为\(f\),上的为\(g\)(等于时都可)。

下面约定\(f[l]\)为在\(f\)线段上\(x=l\)时的纵坐标,
先给该点打上标记\(g\)
如果\(f\)\(g\)全面覆盖,即有\(f[l] < g[l], f[r] < g[r]\),那么不需考虑\(f\)向下,返回。
如果\(f[l] > g[l]\),说明\(f\)在左半部分还是有优势,相对在右半部分一定无优势,所以向\([l, mid]\)递归传下\(f\)
反之,如果\(f[r] > g[r]\),向\([mid+1, r]\)递归传下\(f\)即可。

全局上,在修改时,先找出线段范围对应线段树节点,在从节点开始向下做标记比较。
\(log_2n\)个节点,递归也是深度即\(log_2n\)级,所以总复杂度为\(O(nlog_2^2n)\)

模版题代码:P4097 【模板】李超线段树 / [HEOI2013] Segment

细节部分:
注意要求相同纵坐标还要按编号小输出,不妨直接用编号作标记,
同时等于也要考虑下传
数据范围需要long double

点击查看代码
#include<algorithm>
#include<stdio.h>
#define ll long long
#define N 100005
#define db long double
using namespace std;
ll read() {
	char c=getchar(); ll x=0, y=1;
	while(!(c >= '0' && c <= '9' || c == '-')) c=getchar();
	if(c == '-') y=-1, c=getchar();
	while(c >= '0' && c <= '9') x=x*10+c-'0', c=getchar();
	return x*y;
}
int n;

struct line {
	db k, b;
} li[N];

db calc(int x, ll pos) {
	return li[x].k * pos + li[x].b;
}
struct SMT {
	struct node {
		int l, r;
		int lin;
	} t[N<<2];
	int ls(int x) {return x<<1;}
	int rs(int x) {return x<<1|1;}
	void tree_pre(int x, int l, int r) {
		t[x].l=l, t[x].r=r;
		if(l == r) return;
		int mid = (l+r) >> 1;
		tree_pre(ls(x), l, mid);
		tree_pre(rs(x), mid+1, r);
	}
	void tagd(int x, int l, int r, int seg) {
		int mid = (l+r) >> 1;
		int f = seg, g = t[x].lin;
		if(calc(f, mid) > calc(g, mid)) swap(f, g);
//		printf("%d %d %.2lf %.2lf %.2lf %.2lf\n", l, r, calc(f, l), calc(f, r), calc(g, l), calc(g, r));
		
		if(calc(f, l) < calc(g, l) && calc(f, r) < calc(g, r)) {
			t[x].lin = g;
			return;
		}
		if(calc(f, l) >= calc(g, l)) {
			t[x].lin = g;
			tagd(ls(x), l, mid, f);
		}
		if(calc(f, r) >= calc(g, r)) {
			t[x].lin = g;
			tagd(rs(x), mid+1, r, f);
		}
	}
	void modify(int x, int l, int r, int seg) {
		if(t[x].l >= l && t[x].r <= r) {
			tagd(x, t[x].l, t[x].r, seg);
			return;
		}
		if(t[ls(x)].r >= l) modify(ls(x), l, r, seg);
		if(t[rs(x)].l <= r) modify(rs(x), l, r, seg);
	}
	void query(int x, int des, int &ans, db &num) {
//		printf("%.2f %d\n", calc(t[x].lin, des), t[x].lin);
		if(calc(t[x].lin, des) > num) {
			num = calc(t[x].lin, des);
			ans = t[x].lin;
		}
		else if(calc(t[x].lin, des) == num) {
			ans = min(ans, t[x].lin);
		}
		if(t[x].l == t[x].r) {
			return;
		}
		if(t[ls(x)].r >= des) query(ls(x), des, ans, num);
		else query(rs(x), des, ans, num);
	}
} T;

db xi[N], yi[N];
const ll M1 = 39989, M2 = 1000000000;
int main() {
//	freopen("P4097.in", "r", stdin);
//	freopen("P4097.out", "w", stdout);
	n=read();
	int i;
	int lastans=0;
	li[0] = (line){0, -1e9};
	int n1=0;
	int nn = 10;
	T.tree_pre(1, 1, 50000);
	for(i=1; i<=100000; i++) {
		xi[i] = 0;
		yi[i] = -1e9;
	}
	for(i=1; i<=n; i++) {
		int op=read();
		if(op == 0) {
			int k=read();
			k = (k+lastans-1)%39989 + 1;
			int ans=xi[k]; 
			db num=yi[k];
			T.query(1, k, ans, num);
			printf("%d\n", ans);
//			printf("%.2f\n", num);
			lastans = ans;
		}
		else {
			ll x0=read(), y0=read(), x1=read(), y1=read();
			x0 = (x0+lastans-1)%M1 + 1;
			y0 = (y0+lastans-1)%M2 + 1;
			x1 = (x1+lastans-1)%M1 + 1;
			y1 = (y1+lastans-1)%M2 + 1;
//			printf("%lld %lld %lld %lld\n", x0, y0, x1, y1);
			
			if(x0 > x1) swap(x0, x1), swap(y0, y1);
			n1++;
			if(x0 == x1) {
				if(max(y0, y1) > yi[x0]) yi[x0] = max(y0, y1), xi[x0] = n1; 
			}
			else {
				db k = (db)(y1-y0) / (db)(x1-x0);
				li[n1] = {k, y0-k*x0};
//				printf("%.2f %.2f\n", calc(n1, x0), calc(n1, x1));
				T.modify(1, x0, x1, n1);				
			}

		}
	}
	return 0;
}
posted @ 2024-08-14 15:48  Ian8877  阅读(2)  评论(0编辑  收藏  举报