[笔记]线段树学习笔记

[笔记]线段树学习总结

概念

每个节点以结构体的方式存储,结构体包含以下几个信息:
区间左端点、右端点;(这两者必有)
这个区间要维护的信息(事实际情况而定,数目不等)。
图例:
Alt text

性质

1、每个节点的左孩子区间范围为[l,mid],右孩子为[mid+1,r]

2、对于结点k,左孩子结点为2k,右孩子为2k+1,这符合完全二叉树的性质

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

建树

基本结构:

a.对于二分到的每一个结点,给它的左右端点确定范围。

b.如果是叶子节点,存储要维护的信息。

c.累加。

参考代码

struct node{
	int l,r,w;
}tree[100010];
inline void Build(int l,int r,int k){
	tree[k].l = l,tree[k].r = r;
	if(l == r){
		scanf("%d",&tree[k].w);
		return;
	}
	int mid = (tree[k].l + tree[k].r) / 2;
	Build(l,mid,k * 2);//递归
	Build(mid + 1,r,k * 2 + 1);//递归
	tree[k].w = tree[k * 2].w + tree[k * 2 + 1].w;//累加
	return;
}

注意事项

1.开结构体数组时要开4倍

2.在输入完叶子结点后要return,因为叶子结点不用再递归了。

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

单点查询

基本结构:

与二分查询法基本一致,如果当前枚举的点左右端点相等,即叶子节点,就是目标节点。否则设查询位置为x,当前结点区间范围为l,r,中点为mid。则如果x<=mid,就递归它的左孩子,否则递归它的右孩子

参考代码

inline int Ask_p(int x){
	if(tree[x].l == tree[x].r){//叶子结点
		return tree[x].w;
	}
	int mid = (tree[x].l + tree[x].r) / 2;
	if(mid >= x)
		Ask_p(x * 2);//目标位置比中点靠左,就递归左孩子 
	else Ask_p(x * 2 + 1);//递归右孩子
}

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

单点修改

基本结构:

用单点查询的方法找到目标节点并修改它的权值

参考代码

inline void change(int x,int y,int k){
	if(tree[k].l == tree[k].r){//找到目标节点
		tree[k].w = y;//xiu'gai'quan'zhi
		return;
	}
	int mid = (tree[k].l + tree[k].r) / 2;
	if(mid >= x)
		change(x,y,k * 2);
	else change(x,y,k * 2 + 1);
	tree[k].w = tree[k * 2].w + tree[k * 2 + 1].w;
	return;
} 

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

区间查询

基本结构:

分情况来讨论:

假设l,r为节点区间,x,y为目标区间,则有一下三种情况:


参考代码

inline int Ask_section(int x,int y,int k){
	if(tree[k].l > y || tree[k].r < x)return 0; 
	if(tree[k].l >= x && tree[k].r <= y){
		return tree[k].w;
	}
	int mid = (tree[k].l + tree[k].r) / 2;
	return Ask_section(x,y,k * 2) + Ask_section(x,y,k * 2 + 1); 
}

-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

完整代码在此(乘法和加法)

#include <bits/stdc++.h>
using namespace std;
struct node{
	long long w,l,r;
	long long tag_add = 0,tag_mul = 1;
}tree[600010];    //4 times;
long long ans,n,m,p;
void build(long long l,long long r,long long num){
	tree[num].l = l;tree[num].r = r;
	tree[num].tag_mul = 1;
	if(tree[num].l == tree[num].r){
		cin>>tree[num].w;
		tree[num].w %= p;
		return;
	}
	long long mid = (tree[num].l + tree[num].r) / 2;
	build(l,mid,num * 2);
	build(mid + 1,r,num * 2 + 1);
	tree[num].w = (tree[num * 2].w + tree[num * 2 + 1].w) % p;
	return;
}
void pushdown(long long num){
	tree[num << 1].w = (tree[num << 1].w * tree[num].tag_mul + tree[num].tag_add * (tree[num << 1].r - tree[num << 1].l + 1)) % p;
	tree[num << 1 | 1].w = (tree[num << 1 | 1].w * tree[num].tag_mul + tree[num].tag_add * (tree[num << 1 | 1].r - tree[num << 1 | 1].l + 1)) % p;
	tree[num << 1].tag_mul *= tree[num].tag_mul;
	tree[num << 1].tag_mul %= p;
	tree[num << 1 | 1].tag_mul *= tree[num].tag_mul;
	tree[num << 1 | 1].tag_mul %= p;
	tree[num << 1].tag_add = (tree[num << 1].tag_add * tree[num].tag_mul + tree[num].tag_add) % p;
	tree[num << 1 | 1].tag_add = (tree[num << 1 | 1].tag_add * tree[num].tag_mul + tree[num].tag_add) % p;
	tree[num].tag_mul = 1;
	tree[num].tag_add = 0;
	return;
}
void ask_a(long long num,long long tar_l,long long tar_r){
	if(tree[num].l >= tar_l && tree[num].r <= tar_r){
		ans += tree[num].w;
		ans %= p;
		return;
	}
	pushdown(num);
	long long mid = (tree[num].l + tree[num].r) / 2;
	if(mid >= tar_l)	
		ask_a(num * 2,tar_l,tar_r);
	if(mid < tar_r)
		ask_a(num * 2 + 1,tar_l,tar_r);
}
void add_a_mul(long long num,long long tar_l,long long tar_r,long long w){
	if(tree[num].l >= tar_l && tree[num].r <= tar_r){
		tree[num].tag_mul *= w;
		tree[num].tag_mul %= p;
		tree[num].w *= w;
		tree[num].w %= p;
		tree[num].tag_add *= w;
		tree[num].tag_add %= p;
		return;
	}
	pushdown(num);
	long long mid = (tree[num].l + tree[num].r) / 2;
	if(mid >= tar_l)
		add_a_mul(num * 2,tar_l,tar_r,w);
	if(mid < tar_r)
		add_a_mul(num * 2 + 1,tar_l,tar_r,w);
	tree[num].w = (tree[num * 2].w + tree[num * 2 + 1].w) % p;
}
void add_a_add(long long num,long long tar_l,long long tar_r,long long w){
	if(tree[num].l >= tar_l && tree[num].r <= tar_r){
		tree[num].w += (tree[num].r - tree[num].l + 1) * w;
		tree[num].w %= p;
		tree[num].tag_add += w;
		tree[num].tag_add %= p;
		return;
	}
	pushdown(num);
	long long mid = (tree[num].l + tree[num].r) / 2;
	if(mid >= tar_l)
		add_a_add(num * 2,tar_l,tar_r,w);
	if(mid < tar_r)
		add_a_add(num * 2 + 1,tar_l,tar_r,w);
	tree[num].w = (tree[num * 2].w + tree[num * 2 + 1].w) % p;
}
int main(){
	cin>>n>>m>>p;
	build(1,n,1);
	for(int i = 1;i <= m;i++){
		int opt;
		cin>>opt;
		if(opt == 1){
			int x,y,w;
			cin>>x>>y>>w;
			add_a_mul(1,x,y,w);
		}
		if(opt == 2){
			int x,y,w;
			cin>>x>>y>>w;
			add_a_add(1,x,y,w);
		}
		if(opt == 3){
			int x,y;
			cin>>x>>y;
			ans = 0;
			ask_a(1,x,y);
			ans %= p;
			cout<<ans<<endl;
		}
	}
	return 0;
}

小结

线段树真是个好东西

参考来源

未完待续·········

posted @ 2020-08-21 21:29  czyczy  阅读(174)  评论(0编辑  收藏  举报