算法整理 & 复习:线段树、线段树合并



洛谷博客


一、线段树


1. 线段树基础


P3373 【模板】线段树 2


(1) 定义

#define _L k<<1				//左右子树
#define _R k<<1|1
#define L(x) tree[(x)].L 	//左右端点
#define R(x) tree[(x)].R
#define W(x) tree[(x)].w
#define T(x) tree[(x)].T	//懒惰标记

struct node{
	int L,R,w;
	int T;
}tree[MAXN];


(2) 下传函数及建树

void down(int k){
	T(_L) += T(k);
	T(_R) += T(k);
	W(_L) += T(k)*(R(_L)-L(_L)+1);
	W(_R) += T(k)*(R(_R)-L(_R)+1);
	T(k) = 0;
}

void build(int L, int R, int k){
	L(k) = L;
	R(k) = R;
	if(L == R){
		W(k) = 0;
		return;
	}
	int mid = (L+R)>>1;
	build(L  ,mid,_L);
	build(mid+1,R,_R);
	W(k) = W(_L)+W(_R);
}


(3) 单点修改、单点查询

void update_point(int x, int k, int w){	//x 为目标点
	if(L(k) == R(k)){
		W(k) += w;
		return;
	}
	down(k);
	int mid = (L(k)+R(k))>>1;
	if(x <= mid)	update_point(x,_L,w);
	else			update_point(x,_R,w);
	W(k) = W(_L)+W(_R);
}

int query_point(int x, int k){	//x 为目标点
	if(L(k) == R(k))	return W(k);
	down(k);
	int mid = (L(k)+R(k))>>1;
	if(x <= mid)	return query_point(x,_L);
	else			return query_point(x,_R);
}


(4) 区间修改、区间查询

void update_interval(int l, int r, int k, int w){	//l,r 为目标区间
	if(L(k)>=l && R(k)<=r){
		W(k) += w*(R(k)-L(k)+1);
		T(k) += w;
		return;
	}
	down(k);
	int mid = (L(k)+R(k))>>1;
	if(l <= mid)	update_interval(l,r,_L,w);
	if(r >  mid)	update_interval(l,r,_R,w);
	W(k) = W(_L)+W(_R);
}

int query_interval(int l, int r, int k){
	int ans = 0;
	if(L(k)>=l && R(k)<=r)	return W(k);
	down(k);
	int mid = (L(k)+R(k))>>1;
	if(L <= mid)	ans  = query_interval(l,r,_L);
	if(R >  mid)	ans += query_interval(l,r,_R);
	return ans;
}


2. 线段树求区间最值



(1) 下传函数及建树

void down(int k){
	T(_L) += T(k);	W(_L) += T(k);
	T(_R) += T(k);	W(_R) += T(k);
	T(k) = 0;
}

void build_max(int L, int R, int k){
	L(k) = L;
	R(k) = R;
	if(L == R){
		W(k) = 0;
		return;
	}
	int mid = (L(k)+R(k))>>1;
	build_max(L  ,mid,_L);
	build_max(mid+1,R,_R);
	W(k) = max(W(_L),W(_R));
}


(2) 单点修改

void update_point_max(int x, int k, int w){	//x 为目标点
	if(L(k) == R(k)){
		W(k) += w;
		return;
	}
	down(k);
	int mid = (L(k)+R(k))>>1;
	if(x <= mid)	update_point_max(x,_L,w);
	else			update_point_max(x,_R,w);
	W(k) = max(W(_L),W(_R));
}


(3) 区间修改、区间查询

void upadate_interval_max(int l, int r, int k, int w){	//l,r 为目标区间
	if(L(k)>=l && R(k)<=r){
		W(k) += w;
		T(k) += w;
		return;
	}
	down(k);
	int mid = (L(k)+R(k))>>1;
	if(l <= mid)	upadate_interval_max(l,r,_L,w);
	if(r >  mid)	upadate_interval_max(l,r,_R,w);
	W(k) = max(W(_L),W(_R));
}

int query_interval_max(int l, int r, int k){
	int x=0,y=0;
	if(L(k)>=l && R(k)<=r) return W(k);
	down(k);
	int mid = (L(k)+R(k))>>1;
	if(l <= mid)	x = query_interval_max(l,r,_L);
	if(r >  mid)	y = query_interval_max(l,r,_R);
	return max(x,y);
}


3. 线段树求区间连续最大子段和


SP1716 GSS3 - Can you answer these queries III


(1) 定义

#define mL(x) tree[(x)].maxL // 最大前缀和
#define mR(x) tree[(x)].maxR // 最大后缀和
#define mS(x) tree[(x)].maxS // 最大连续子段和
#define sm(x) tree[(x)].sum  // 区间和

struct node{
	int L,R;
	int maxL,maxR,maxS,sum;
}tree[MAXN];


(2) 更新函数及建树

void update_tree_sum(int k){
	sm(k) = sm(_L)+sm(_R);
	mL(k) = max(mL(_L),sm(_L)+mL(_R));
	mR(k) = max(mR(_R),sm(_R)+mR(_L));
	mS(k) = max(max(mS(_L),mS(_R)),mL(_R)+mR(_L));
}

void build_sum(int L, int R, int k, int i){	//a[i] 为原数列
	L(k) = L;
	R(k) = R;
	if(L == R){
		mL(k) = mR(k) = mS(k) = sm(k) = a[i];
		return;
	}
	int mid = (L(k)+R(k))>>1;
	build_sum(L  ,mid,_L,i);
	build_sum(mid+1,R,_R,i);
	update_tree_sum(k);
}


(3) 单点修改

void update_point_sum(int x, int k, int w){
	if(L(k) == R(k)){
		mL(k) = mR(k) = mS(k) = sm(k) = w;
		return;
	}
	int mid = (L(k)+R(k))>>1;
	if(x <= mid)	update_point_sum(x,_L,w);
	else			update_point_sum(x,_R,w);
	update_tree_sum(k);
}


(4) 区间查询

node query_interval_sum(int l, int r, int k){
	node Ls,Rs,ans;
    if(L(k)>=l && R(k)<=r) return tree[k];
    int mid = (L(k)+R(k))/2;
    	 if(r <= mid)	return query_interval_sum(L,R,_L);
    else if(l >  mid)   return query_interval_sum(L,R,_R);
    else{
        Ls = query_interval_sum(L,R,_L);
        Rs = query_interval_sum(L,R,_R);
        ans.sum  = Ls.sum+Rs.sum;
        ans.maxL = max(Ls.maxL,Ls.sum+Rs.maxL);
        ans.maxR = max(Rs.maxR,Rs.sum+Ls.maxR);
        ans.maxS = max(max(Ls.maxS,Rs.maxS),Ls.maxR+Rs.maxL);
        return ans;
    }
}


4. 区间平均数、方差


P2122 还教室


(1) 定义

#define S(x) tree[(x)].S;

struct node{
	int L,R,T;
	int S,sum; //平方和,区间和
}tree[MAXN];


(2) 下传函数、更新函数及建树

void down(int k){
	int T = T(k);
	T(_L) += T;
	T(_R) += T;
	S(_L)  = (R(_L)-L(_L)+1)*T*T+2*T*sm(_L);
	S(_R)  = (R(_R)-L(_L)+1)*T*T+2*T*sm(_R);
	sm(_L) = (R(_L)-L(_L)+1)*T;
	sm(_R) = (R(_R)-L(_R)+1)*T;
	T(k)   = 0;
}

void update_tree_S(int k){
	sm(k) = sm(_L)+sm(_R);
	S (k) = S (_L)+S (_R);
}

void build_tree_S(int L, int R, int k, int i){
	L(k) = L;
	R(k) = R;
	if(L == R){
		sm(k) = a[i];
		S (k) = a[i]*a[i];
		return;
	}
	int mid = (L(k)+R(k))>>1;
	build_tree_S(L  ,mid,_L,i);
	build_tree_S(mid+1,R,_R,i);
	update_tree_S(k);
}


(3) 区间修改、区间查询

void update_interval_S(int l ,int r, int k, int w){
	if(L(k)>=l && R(k)<=r){
		S (k) += w*(R-L+1)*w+2*w*sm(k);
		sm(k) += w*(R-L+1);
		T (k) += w;
		return;
	}
	down(k);
	int mid = (L(k)+R(k))>>1;
	if(l <= mid) update_interval_S(l,r,_L,w);
	if(r >  mid) update_interval_S(l,r,_R,w);
	update_tree_S(k);
}

node query_interval_S(int l, int r, int k){
	node x,y,ans;
	if(L(k)>=l && R(k)<=r) return tree[k];
	down(k);
	int mid = (L(k)+R(k))>>1;
	if(l <= mid) x = query_interval_S(l,r,_L);
	if(r >  mid) y = query_interval_S(l,r,_R);
	ans.sum = x.sum+y.sum;
	ans.S = x.S+y.S;
}


5. 线段树求最长严格上升子序列(subsequence)


P4198 楼房重建


(1) 定义

#define Mx(x) tree[(x)].Max
#define Ln(x) tree[(x)].len

struct node{
	int L,R;
	int Max,len; //区间最值,子序列长度
}tree[MAXN];


(2) 更新函数及建树

int update_tree_sub(int k, int w){
	if(L(k) == R(k))  return Mx(k) > w;
	if(w  >= Mx(_L))  return update_tree_sub(_R,w);
	return Ln(k)-Ln(_L)+update_tree_sub(_L,w);
}

void build_tree_sub(int L, int R, int k){
	L(k) = L;
	R(k) = R;
	if(L == R) return;
	int mid = (L(k)+R(k))>>1;
	build_tree_sub(L,mid,_L);
	build_tree_sub(mid+1,R,_R);
}


(3) 单点修改

void update_point_sub(int x, int k, int w){
	if(L(k) == R(k)){
		Mx(k) = w;
		Ln(k) = 1;
		return;
	}
	int mid = (L(k)+R(k))>>1;
	if(x >= mid) update_point_sub(x,_L,w);
	if(x <  mid) update_point_sub(x,_R,w);
	Mx(k) = max(Mx(_L),Mx(_R));
	Ln(k) = Ln(_L)+update_tree_sub(_R,Mx(_L));
}


6. 区间修改转单点修改

利用差分,再维护一个区间前缀和就可以了



7. XOR

区间修改
区间查询:1 的个数

我不会。



8. 线段树求第 k 个值


P4879 ycz的妹子

利用权值线段树的思想,当这个点有值时 t r e e [ k ] . c n t tree[k].cnt tree[k].cnt 为 1 ,统计它的和,查询第 k k k 个值时若 k < = t r e e [ _ L ] . c n t k <= tree[\_L].cnt k<=tree[_L].cnt ,说明第 k k k 个数在左边,否则在右边。

注意,查询右子树时要将 k k k 减去 t r e e [ _ L ] . c n t t r e e [ _ L ] . c n t tree[\_L].cnttree[\_L].cnt tree[_L].cnttree[_L].cnt


(1) 定义

#define int long long

struct node{
    int L,R;
    int w,cnt;
}tree[MAXN];


(2) 更新函数与建树

void update(int k){
    W(k) = W(_L)+W(_R);
    tree[k].cnt = tree[_L].cnt+tree[_R].cnt;
}

void build(int L, int R, int k){
    L(k) = L;
    R(k) = R;
    if(L == R){
        W(k) = a[L];
        if(a[L]) tree[k].cnt = 1;
        return;
    }
    int mid = (L+R)>>1;
    build(L,mid,_L);
    build(mid+1,R,_R);
    update(k);
}


(3) 单点更新、添加、删除

void update_point(int k, int x, int w){
    if(L(k) == R(k)){
        W(k) -= w;
        return;
    }
    int mid = (L(k)+R(k))>>1;
    if(x <= mid) update_point(_L,x,w);
    else         update_point(_R,x,w);
    update(k);
}

void add_point(int k, int x, int w){
    if(L(k) == R(k)){
        W(k) = w;
        tree[k].cnt = 1;
        return;
    }
    int mid = (L(k)+R(k))>>1;
    if(x <= mid) add_point(_L,x,w);
    else         add_point(_R,x,w);
    update(k);
}

void Delete(int k, int x){
    if(L(k)==R(k) && x==tree[k].cnt){
        W(k) = 0;
        tree[k].cnt--;
        return;
    }
    if(x <= tree[_L].cnt)   Delete(_L,x);
    else                    Delete(_R,x-tree[_L].cnt);
    update(k);
}



二、线段树合并(动态开点线段树)


P3224 [HNOI2012]永无乡


#include <stdio.h>
#include <iostream>
using namespace std;
#define MAXN 3000005
#define Ls(x) tree[(x)].ls
#define Rs(x) tree[(x)].rs
#define L(x) tree[(x)].L
#define R(x) tree[(x)].R

int n,m,cnt;
int prn[MAXN],root[MAXN];

struct node{
	int ls,rs.L,R,w,id;
}tree[4*MAXN];

int find(int x){
	if(prn[x] == x) return x;
	return prn[x] = find(prn[x]);
}

int build(int k, int l, int r, int rk, int i){
	if(!k) k = ++cnt;
	L(k) = l;
	R(k) = r;
	if(l == r){
		tree[k].id = i;
		tree[k].w = 1;
		return k;
	}
	int mid = (l+r)>>1;
	if(rk <= mid) Ls(k) = build(Ls(k),l  ,mid,rk,i);
	else		  Rs(k) = build(Rs(k),mid+1,r,rk,i);
	tree[k].w = tree[Ls(k)].w+tree[Rs(k)].w;
	return k;
}

int merge(int x, int y){
	if(!x) return y;
	if(!y) return x;
	if(L(x) == R(x)){
		tree[x].w += tree[y].w;	
		if(!tree[x].id)
			tree[x].id = tree[y].id;
		return x;
	}
	Ls(x) = merge(Ls(x),Ls(y));
	Rs(x) = merge(Rs(x),Rs(y));
	tree[x].w = tree[Ls(x)].w+tree[Rs(x)].w;
	return x;
}

int query(int k, int rk){
	if(tree[k].w<rk || !k) return -1;
	if(L(k) == R(k)) return tree[k].id;
	if(tree[Ls(k)].w >= rk) return query(Ls(k),rk);
	else 					return query(Rs(k),rk-tree[Ls(k)].w);
}

int main(void){
	int x,y;
	cin >> n >> m;
	for(int i=1;i<=n;i++){
		cin >> x;
		prn[i] = i;
		root[i] = build(root[i],1,n,x,i);
	}
	for(int i=1;i<=m;i++){
		cin >> x >> y;
		int t1 = find(x);
		int t2 = find(y);
		prn[t2] = t1;
		root[t1] = merge(root[t1],root[t2]);
	}
	cin >> m;
	for(int i=1;i<=m;i++){
		char p;
		cin >> p >> x >> y;
		if(p == 'Q'){
			int t1 = find(x);
			cout << query(root[t1],y) << endl;
		}
		if(p == 'B'){
			int t1 = find(x);
			int t2 = find(y);
			if(t1 == t2) continue;
			prn[t2] = t1;
			root[t1] = merge(root[t1],root[t2]);
		}
	}
	return 0;
}





  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SP FA

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值