线段树

第一版

关于线段树本身

  1. 区间查询,单点修改
  2. 单点查询,区间修改
  3. lazy思想

关于例题

  1. 有一个LG的模板

我在恍惚之间又想起了那件事,仿佛就在昨天。

学校,午饭后,两个人在校园里闲逛,迎面遇上yjx,说:"你们是两个人,真好。我谔谔。

多说无益,从此,便是陌路人。总是无中生有,又有什么意思。

正文

原题链接

我们先来了解一下线段树

定义

度娘上是这么说的 : 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。

限制:此处以询问区间和为例。实际上线段树可以处理很多符合结合律的操作。(比如说加法,a[1]+a[2]+a[3]+a[4]=(a[1]+a[2])+(a[3]+a[4]))

线段树之所以称为“树”,是因为其具有树的结构特性。

就以一个图片为例,你建出来的线段树大概是这样的

表示

对于每一个子节点而言,都表示整个序列中的一段子区间;对于每个叶子节点而言,都表示序列中的单个元素信息;子节点不断向自己的父亲节点传递信息,而父节点存储的信息则是他的每一个子节点信息的整合。

我们来一波逐步肢解线段树

线段树的实现

节点表示与儿子节点

typedef long long ll;
#define fake ll
struct TreeNode{
	fake mul_lazy,add_lazy,sum,l,r;
    //l表示区间左端点,同理有r,mul_lazy表示乘的懒惰标记,同理有add_lazy,sum表示区间和
}tree[Maxn*4];
fake ls(fake k){return k<<1;}
fake rs(fake k){return k<<1|1;}

在线段树里,实际空间只能用到2*Maxn,但是为了避免越界,一般来说开到4*Maxn

这里用位运算会快的。ls表示左节点,rs表示右节点

维护

push_up_sum()就是一个把儿子的信息传递给父亲

void push_up_sum(fake k){tree[k].sum=tree[ls(k)].sum+tree[rs(k)].sum;}

建树

build函数

那么对于建树,由于二叉树自身的父子节点之间的可传递关系,所以可以考虑递归建树,并且在建树的同时,我们应该维护父子节点的关系:

void build(fake k,fake l,fake r){
	tree[k].l=l,tree[k].r=r;tree[k].mul_lazy=1;
	if(l==r){tree[k].sum=a[l];return ;}
	fake mid=(l+r)>>1;
	build(ls(k),l,mid);
	build(rs(k),mid+1,r);
	push_up_sum(k); 
}

区间修改

chS_addchS_mul

先来谈谈懒惰标记

首先,懒标记的作用是记录每次、每个节点要更新的值,但线段树的优点不在于全记录(全记录依然很慢qwq),而在于传递式记录:

可以自己画图意会

void f(fake k,fake add,fake mul){
	tree[k].sum=((tree[k].sum*mul)+(tree[k].r-tree[k].l+1)*add);
	tree[k].mul_lazy=tree[k].mul_lazy*mul;
	tree[k].add_lazy=(tree[k].add_lazy*mul+add);//更新一个点的懒惰标记 
}
void push_down(fake k){
	f(ls(k),tree[k].add_lazy,tree[k].mul_lazy);
	f(rs(k),tree[k].add_lazy,tree[k].mul_lazy);
	tree[k].add_lazy=0;tree[k].mul_lazy=1;
}

就是说你把一个节点的懒惰标记传递给儿子的操作,自己想想操作顺序

注意 懒惰标记是什么和本身无关,只关于传给节点的信息

于是按照push_up_sum的逆向思想,可以查找此区间

void chS_add(fake k,fake l,fake r,fake x){
	if(l<=tree[k].l&&tree[k].r<=r){
		tree[k].add_lazy=(tree[k].add_lazy+x);
		tree[k].sum=(tree[k].sum+(tree[k].r-tree[k].l+1)*x);
		return ;
	}
	push_down(k);
	fake mid=(tree[k].l+tree[k].r)>>1;
	if(l<=mid) chS_add(ls(k),l,r,x);
	if(r>mid) chS_add(rs(k),l,r,x);
	push_up_sum(k);
	return ;
}
void chS_mul(fake k,fake l,fake r,fake x){
	if(l<=tree[k].l&&tree[k].r<=r){
		tree[k].mul_lazy=(tree[k].mul_lazy*x);
		tree[k].add_lazy=(tree[k].add_lazy*x);
		tree[k].sum=(tree[k].sum*x);
		return ;
	}
	push_down(k);
	fake mid=(tree[k].l+tree[k].r)>>1;
	if(l<=mid) chS_mul(ls(k),l,r,x);
	if(r>mid) chS_mul(rs(k),l,r,x);
	push_up_sum(k);
	return ; 
}

区间查询

我们也用chS的思想,可以简单解决

fake query(fake k,fake l,fake r){
	if(l<=tree[k].l&&tree[k].r<=r){return tree[k].sum;}
	fake mid=(tree[k].l+tree[k].r)>>1;;
	fake ret=0;
	push_down(k);
	if(l<=mid) ret+=query(ls(k),l,r);
	if(r>mid) ret+=query(rs(k),l,r);
	return ret;
}

AC代码(线段树2)

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <fstream>
#include <stdio.h>
#include <math.h>
#include <stack>
#include <map>
#include <queue>
#include <vector>
#include <deque>
#include <list>
#include <set>
#include <bits/stdc++.h>
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
#define fake ll
#define size 666 
using namespace std;
fake read(){
	fake x=0;
	char ch=getchar();
	while(ch<'0'||ch>'9') ch=getchar();
	while(ch<='9'&&ch>='0'){
		x=(x<<1)+(x<<3)+(ch-'0');
		ch=getchar();
	}
	return x;
}
const int Maxn=1e5+11;
struct TreeNode{
	fake mul_lazy,add_lazy,sum,l,r;
}tree[Maxn*4];
fake a[Maxn],n,m,p,b,x,y;
fake ls(fake k){return k<<1;}
fake rs(fake k){return k<<1|1;}
void build(fake k,fake l,fake r);
void push_up_sum(fake k);//献上和 
void push_down(fake k);//向下推进懒惰标记
void f(fake k,fake add,fake mul);
void chS_mul(fake k,fake l,fake r,fake x);
void chS_add(fake k,fake l,fake r,fake x);
fake query(fake k,fake l,fake r);
void f(fake k,fake add,fake mul){
	tree[k].sum=((tree[k].sum*mul)+(tree[k].r-tree[k].l+1)*add)%p;
	tree[k].mul_lazy=tree[k].mul_lazy*mul%p;
	tree[k].add_lazy=(tree[k].add_lazy*mul+add)%p;//更新一个点的懒惰标记 
}
void push_up_sum(fake k){tree[k].sum=(tree[ls(k)].sum+tree[rs(k)].sum)%p;}
void build(fake k,fake l,fake r){
	tree[k].l=l,tree[k].r=r;tree[k].mul_lazy=1;
	if(l==r){tree[k].sum=a[l]%p;return ;}
	fake mid=(l+r)>>1;
	build(ls(k),l,mid);
	build(rs(k),mid+1,r);
	push_up_sum(k); 
}
void push_down(fake k){
	f(ls(k),tree[k].add_lazy,tree[k].mul_lazy);
	f(rs(k),tree[k].add_lazy,tree[k].mul_lazy);
	tree[k].add_lazy=0;tree[k].mul_lazy=1;
}
void chS_add(fake k,fake l,fake r,fake x){
	if(l<=tree[k].l&&tree[k].r<=r){
		tree[k].add_lazy=(tree[k].add_lazy+x)%p;
		tree[k].sum=(tree[k].sum+(tree[k].r-tree[k].l+1)*x)%p;
		return ;
	}
	push_down(k);
	fake mid=(tree[k].l+tree[k].r)>>1;
	if(l<=mid) chS_add(ls(k),l,r,x);
	if(r>mid) chS_add(rs(k),l,r,x);
	push_up_sum(k);
	return ;
}
void chS_mul(fake k,fake l,fake r,fake x){
	if(l<=tree[k].l&&tree[k].r<=r){
		tree[k].mul_lazy=(tree[k].mul_lazy*x)%p;
		tree[k].add_lazy=(tree[k].add_lazy*x)%p;
		tree[k].sum=(tree[k].sum*x)%p;
		return ;
	}
	push_down(k);
	fake mid=(tree[k].l+tree[k].r)>>1;
	if(l<=mid) chS_mul(ls(k),l,r,x);
	if(r>mid) chS_mul(rs(k),l,r,x);
	push_up_sum(k);
	return ; 
}
fake query(fake k,fake l,fake r){
	if(l<=tree[k].l&&tree[k].r<=r){return tree[k].sum%p;}
	fake mid=(tree[k].l+tree[k].r)>>1;;
	fake ret=0;
	push_down(k);
	if(l<=mid) ret+=query(ls(k),l,r);
	if(r>mid) ret+=query(rs(k),l,r);
	return ret%p;
}
int main(){
	freopen("Interval Tree.in","r",stdin);
	n=read();m=read();p=read();
	for(int i=1;i<=n;i++) a[i]=read();
	build(1,1,n);
	for(int i=1;i<=m;i++){
		b=read();x=read();y=read();
		if(b==1) chS_mul(1,x,y,read()); 
		else if(b==2) chS_add(1,x,y,read());
		else if(b==3) printf("%lld
",query(1,x,y));
	}
	return 0;
} 

愿线段树陪你出走半生,归来仍是少年。

原文地址:https://www.cnblogs.com/zhltao/p/12303807.html