李超線段樹

Ian8877發表於2024-08-14

用途:

用於二維座標系維護多條線段。

演算法:

本質上是採用標記永久化,
對每個線段樹節點維護一個標記表示該區間存在這一條線段,
查詢時從上到下經過節點的標記即為該橫座標上可能經過的線段。
下面需在標記(線段)間的比較上作考慮:建議畫圖理解
此時對於一個區間\([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;
}

相關文章