OI模板 线段树

OI模板 线段树

线段树(区间改、区间查)with lazy tag

//P3372
#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 10;
struct SegmentTree{
	struct Node{ int l, r; long long sum, lazy; } T[N*4];
	int a[N], n;
	inline void update(int p){
		T[p].sum = T[p<<1].sum + T[p<<1|1].sum; 
	}
	inline void spread(int p){
		if(T[p].lazy){
			T[p<<1].sum += T[p].lazy * (T[p<<1].r - T[p<<1].l + 1);
			T[p<<1|1].sum += T[p].lazy * (T[p<<1|1].r - T[p<<1|1].l + 1);
			T[p<<1].lazy += T[p].lazy;
			T[p<<1|1].lazy += T[p].lazy;
			T[p].lazy = 0;
		}
	}
	inline void buildtree(int p, int l, int r){
		T[p].l = l, T[p].r = r;
		if(l == r){ T[p].sum = a[l]; return; }
		int mid = l + r >> 1;
		buildtree(p << 1, l, mid);
		buildtree(p << 1 | 1, mid + 1, r);
		update(p);
	}
	inline void modify(int p, int x, long long v){
		if(T[p].l == T[p].r){ T[p].sum = v; return; }
		spread(p);
		int mid = T[p].l + T[p].r >> 1;
		if(x <= mid) modify(T[p].l, x, v);
		else modify(p << 1 | 1, x, v);
		update(p);
	}
	inline void change(int p, int l, int r, long long v){
		if(l <= T[p].l && T[p].r <= r){
			T[p].sum += v * (T[p].r - T[p].l + 1);
			T[p].lazy += v;
			return; 
		}
		spread(p);
		int mid = T[p].l + T[p].r >> 1;
		if(l <= mid) change(p << 1, l, r, v);
		if(mid < r) change(p << 1 | 1, l, r, v);
		update(p);
	}
	inline long long ask(int p, int l, int r){
		if(l <= T[p].l && T[p].r <= r) return T[p].sum;
		spread(p);
		int mid = T[p].l + T[p].r >> 1;
		long long val = 0;
		if(l <= mid) val += ask(p << 1, l, r);
		if(mid < r) val += ask(p << 1 | 1, l, r);
		return val;
	}
} T;

query 函数另一种写法:不用新变量的写法。

inline long long ask(int p, int l, int r){
	if(l <= T[p].l && T[p].r <= r) return T[p].sum;
	spread(p);
	int mid = T[p].l + T[p].r >> 1;
	if(l <= mid && mid < r) return ask(p << 1, l, r) + ask(p << 1 | 1, l, r);
	else if(l <= mid) return ask(p << 1, l, r);
	else if(mid < r) return ask(p << 1 | 1, l, r);
	return 0;
}

线段树基础模型

1.最大子段和

维护 a l l _ m a x , l e f t _ m a x , r i g h t _ m a x , s u m all\_max,left\_max,right\_max,sum all_max,left_max,right_max,sum

inline void update(int n){
		t[n].sum=t[n<<1].sum+t[n<<1|1].sum;
		t[n].lm=max(t[n<<1].lm,t[n<<1].sum+t[n<<1|1].lm);
		t[n].rm=max(t[n<<1|1].rm,t[n<<1].rm+t[n<<1|1].sum);
		t[n].am=max(max(t[n<<1].am,t[n<<1|1].am),t[n<<1].rm+t[n<<1|1].lm);
		return;
	}

query 函数中可以返回一个节点而不是一个值。

有时候会套二分,例子:[SHOI2015]脑洞治疗仪。

2.扫描线

  1. 取出 n n n 个矩形的左右边界,左边界记为 ( x 1 , y 1 , y 2 , 1 ) (x_1,y_1,y_2,1) (x1,y1,y2,1),右边界记为 ( x 2 , y 1 , y 2 , − 1 ) (x_2,y_1,y_2,-1) (x2,y1,y2,1)(不妨设 x 1 < x 2 , y 1 < y 2 x_1<x_2,y_1<y_2 x1<x2,y1<y2),把这 2 n 2n 2n 个四元组按照 x x x 递增排序。

  2. y y y 进行离散化,设 v a l ( y ) val(y) val(y) 表示 y y y 离散化后的值, r a w ( i ) raw(i) raw(i) 表示 i i i 对应的 y y y。设离散化后对应的 y y y 坐标值有 m m m 个。

  3. 扫描线最多分成 m − 1 m-1 m1 段,其中第 i i i 段为区间 [ r a w ( i ) , r a w ( i + 1 ) ] [raw(i),raw(i+1)] [raw(i),raw(i+1)]。使用数组 c [ i ] c[i] c[i] 记录扫描线上第 i i i 段被覆盖的次数。

  4. 扫描 2 n 2n 2n 个四元组,设当前四元组为 ( x , y 1 , y 2 , k ) (x,y_1,y_2,k) (x,y1,y2,k),将 c [ v a l ( y 1 ) ] , c [ v a l ( y 1 ) + 1 ] , . . . , c [ v a l ( y 2 ) − 1 ] c[val(y_1)],c[val(y_1)+1],...,c[val(y_2)-1] c[val(y1)],c[val(y1)+1],...,c[val(y2)1] 这些值都加上 k k k

  5. 此时,如果下一个四元组的横坐标为 x 2 x_2 x2,则扫描线从 x x x 扫到 x 2 x_2 x2 时,被覆盖的长度为 ∑ c [ i ] > 0 ( r a w ( i + 1 ) − r a w ( i ) ) \sum\limits_{c[i]>0}(raw(i+1)-raw(i)) c[i]>0(raw(i+1)raw(i)),令最终的答案 a n s ans ans 累加上 ( x 2 − x ) ∑ c [ i ] > 0 ( r a w ( i + 1 ) − r a w ( i ) ) (x_2-x)\sum\limits_{c[i]>0}(raw(i+1)-raw(i)) (x2x)c[i]>0(raw(i+1)raw(i))

  6. 总复杂度 O ( n 2 ) O(n^2) O(n2)

  7. 使用线段树维护 c c c 数组,复杂度优化至 O ( n log ⁡ n ) O(n\log n) O(nlogn)

  8. 可以不使用延迟标记。在线段树的每个节点上维护:节点代表区间被矩形覆盖长度 l e n len len,节点自身被覆盖的次数 c n t cnt cnt。对于四元组 ( x , y 1 , y 2 , k ) (x,y_1,y_2,k) (x,y1,y2,k),在 [ v a l ( y 1 ) , v a l ( y 2 ) − 1 ] [val(y_1),val(y_2)-1] [val(y1),val(y2)1] 上执行区间修改,把这些节点的 c n t cnt cnt k k k

  9. 对于线段树任意一个节点 [ l , r ] [l,r] [l,r],若 c n t > 0 cnt>0 cnt>0,则 l e n len len 等于 r a w ( r + 1 ) − r a w ( l ) raw(r+1)-raw(l) raw(r+1)raw(l)。否则,该点 l e n len len 等于两个子节点的 l e n len len 之和。在一个节点的 c n t cnt cnt 值被修改,以及线段树从下往上传递信息时,更新 l e n len len 值,根节点的 l e n len len 就是整个扫描线上被覆盖的长度。

const int N = 1;
struct ScanLine{
	double x, y, z; int k;
	inline bool operator < (const ScanLine &b) const { return x < b.x; }
} a[N*2];
struct Node{ int l, r, cnt; double len; } T[N*8];
map<double, int> val;
double raw[N*2];
int n, m;

inline void build(int p, int l, int r){
	T[p].l = l, T[p].r = r, T[p].cnt = 0, T[p].len = 0;
	int mid = l + r >> 1;
	if(l != r) build(p << 1, l, mid), build(p << 1 | 1, mid + 1, r);
}
inline void change(int p, int l, int r, double k){
	if(l <= T[p].l && T[p].r <= r){
		if(T[p].cnt += k) T[p].len = raw[T[p].r+1] - raw[T[p].l];
		else if(T[p].l == T[p].r) T[p].len = 0;
		else T[p].len = T[p<<1].len + T[p<<1|1].len;
		return;
	}
	int mid = T[p].l + T[p].r >> 1;
	if(l <= mid) change(p << 1, l, r, k);
	if(r > mid) change(p << 1 | 1, l, r, k);
	if(T[p].cnt) T[p].len = raw[T[p].r+1] - raw[T[p].l];
	else T[p].len = T[p<<1].len + T[p<<1|1].len; 
}

inline double solve(){
	scanf("%d", &n);
	for(int i = 1; i <= n; ++ i){
		double y, z;
		scanf("%lld %lld %lld %lld", &a[i*2-1].x, &y, &a[i*2].x, &z);
		raw[i*2-1] = a[i*2-1].y = a[i*2].y = y;
		raw[i*2] = a[i*2-1].z = a[i*2].z = z;
		a[i*2-1].k = 1, a[i*2].k = -1; 
	}
	n <<= 1;
	sort(raw + 1, raw + n + 1);
	m = unique(raw + 1, raw + n + 1) - (raw + 1);
	for(int i = 1; i <= m; ++ i) val[raw[i]] = i;
	sort(a +1, a + n + 1);
	build(1, 1, m - 1);
	double ans = 0;
	for(int i = 1; i < n; ++ i){
		int y = val[a[i].y], z = val[a[i].z] - 1;
		change(1, y, z, a[i].k);
		ans += T[1].len * (a[i+1].x - a[i].x);
	}
	return ans;
}

动态开点 + 线段树合并

代码节选自P4556 [Vani有约会]雨天的尾巴 /【模板】线段树合并AC代码。

struct Node{ int l, r, dat, pos; } T[N*80]; int rt[N], cnt;
void init(){ for(int i = 1; i <= n; ++ i) rt[i] = ++ cnt; }
void pushup(int p){
	T[p].dat = max(T[T[p].l].dat, T[T[p].r].dat);
	T[p].pos = T[T[p].l].dat >= T[T[p].r].dat ? T[T[p].l].pos : T[T[p].r].pos;
}
void modify(int p, int l, int r, int pos, int val){
	if(l == r){ T[p].dat += val, T[p].pos = T[p].dat ? l : 0; return; }
	int mid = l + r >> 1;
	if(pos <= mid){
		if(!T[p].l) T[p].l = ++ cnt;
		modify(T[p].l, l, mid, pos, val);
	} else {
		if(!T[p].r) T[p].r = ++ cnt;
		modify(T[p].r, mid + 1, r, pos, val);
	}
	pushup(p);
}
int merge(int a, int b, int l, int r){
	if(!a || !b) return a + b;
	if(l == r){ T[a].dat += T[b].dat, T[a].pos = T[a].dat ? l : 0; return a; }
	int mid = l + r >> 1;
	T[a].l = merge(T[a].l, T[b].l, l, mid);
	T[a].r = merge(T[a].r, T[b].r, mid + 1, r);
	pushup(a); return a;
}

可持久化数组


图片来自 https://www.luogu.com.cn/blog/juruohyfhaha/p3919-mu-ban-ke-chi-jiu-hua-shuo-zu-chu-bu-tan-jiu-zhu-xi-shu

const int N = 1e6 + 10;
struct PersistableSegmentTree{
	#define MID (l+r>>1)
	#define LSON T[x].l, l, MID
	#define RSON T[x].r, MID+1, r
	struct Node{ int l, r, val; } T[N*20];
	int a[N*20], n, cnt, root[N];
	int newnode(int x){ T[++cnt] = T[x]; return cnt; }
	int buildtree(int x, int l, int r){
		x = ++ cnt;
		if(l == r){ T[x].val = a[l]; return x; }
		T[x].l = buildtree(LSON);
		T[x].r = buildtree(RSON);
		return x;
	}
	int update(int x, int l, int r, int pos, int val){
		x = newnode(x);
		if(l == r){ T[x].val = val; return x; } 
		if(pos <= MID) T[x].l = update(LSON, pos, val);
		else T[x].r = update(RSON, pos, val);
		return x;
	}
	int query(int x, int l, int r, int pos){
		if(l == r) return T[x].val;
		if(pos <= MID) return query(LSON, pos);
		else return query(RSON, pos);
	}
	void Init(){ root[0] = buildtree(0, 1, n); }
	//v:查询/修改基于的版本
	//i:新建版本编号
	void Update(int v, int i, int pos, int val){
		root[i] = update(root[v], 1, n, pos, val);
	}
	int Query(int v, int i, int pos){
		return query(root[i] = root[v], 1, n, pos);
	}
} Tree;

李超树

const int N = 1e5 + 10, T = 39989;
const double eps = 1e-9;
double k[N], b[N];
int tag[N*4], tot;

double calc(int id, int d){
	return b[id] + k[id] * d;
}
void update(int p, int l, int r, int u){
	int &v = tag[p], mid = l + r >> 1;
	if(calc(u, mid) > calc(v, mid) + eps){
		swap(u, v);
	}
	double t1 = calc(u, l) - calc(v, l), t2 = calc(u, r) - calc(v, r);
	if(t1 > eps || (fabs(t1) < eps && u < v)){
		update(p<<1, l, mid, u);
	}
	if(t2 > eps || (fabs(t2) < eps && u < v)){
		update(p<<1|1, mid+1, r, u);
	}
}
void fp(int p, int l, int r, int ql, int qr, int x){
	if(ql <= l && r <= qr){
		update(p, l, r, x);
	} else {
		int mid = l + r >> 1;
		if(ql <= mid){
			fp(p<<1, l, mid, ql, qr, x);
		}
		if(mid < qr){
			fp(p<<1|1, mid+1, r, ql, qr, x);
		}
	}
}
void add(int x, int y, int xx, int yy){
	++ tot;
	if(x == xx){
		k[tot] = 0;
		b[tot] = max(y, yy);
	} else {
		k[tot] = 1.0 * (yy - y) / (xx - x);
		b[tot] = y - k[tot] * x;
	}
	fp(1, 1, T, x, xx, tot);
}
pair<double, int> mx(pair<double, int> x, pair<double, int> y){
	if(x.first + eps < y.first){
		return y;
	}
	if(x.first > y.first + eps){
		return x;
	}
	return x.second < y.second ? x : y;
}
pair<double, int> query(int p, int l, int r, int x){
	if(x > r || x < l){
		return make_pair(0, T+1);
	}
	int mid = l + r >> 1;
	double res = calc(tag[p], x);
	if(l == r){
		return make_pair(res, tag[p]);
	}
	return mx(make_pair(res, tag[p]), mx(query(p<<1, l, mid, x), query(p<<1|1, mid+1, r, x)));
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值