[笔记]线段树学习总结
概念
每个节点以结构体的方式存储,结构体包含以下几个信息:
区间左端点、右端点;(这两者必有)
这个区间要维护的信息(事实际情况而定,数目不等)。
图例:
性质
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;
}