人生中第一次写博客,有亿点点点紧张,写的不好还望大家海涵(萌新瑟瑟发抖)。
线段树是一棵平衡二叉叉树,树的每个节点代表了一个区间的某个特征(最大值、最小值、或者是区间和)。对于每个节点而言(非叶节点),其两个子节点代表了其左右两个子区间的区间特征,而每个叶节点代表了单独的一个元素(区间左右相等)。其实现方法主要有数组实现和指针实现(本文以数组实现为例)。
先考虑每个节点需要的元素:1.节点数值(毋庸置疑)2.区间左右节点(方便查找)
所以线段树的建树代码如下:
struct tree
{
int left,right,value;//分别表示其代表的左右区间以及区间的特征值
}s[800001];
好,接下来我们考虑如何实现线段树的逻辑结构(以区间和为例),其母节点代表一个区间的最大值,而其子节点代表两个子区间的最大值,对于数组[1,2,3,4,5],他的逻辑图应该长这样:
越上层的节点对应的区间范围越大。
对于每个节点i其左节点2*i,右节点2*i+1,分别对应于其左子区间[left,(left+right)/2],右区间[(left+right)/2+1,right],直到left==right(叶节点)。
既然这样的话我们该如何建立一棵线段树呢?
考虑递归实现:
void build(int now,int left,int right)
{
s[now].left=left,s[now].right=right;//明确每个节点的范围
if(left==right)//找到叶节点
{
cin>>s[now].value;//输入叶节点的值并返回
return;
}
int mid=(left+right)/2;
build(now*2,left,mid);
build(now*2+1,mid+1,right);//对于非叶节点,递归的建立其左右子节点
s[now].value =s[now*2].value+s[now*2+1].value;//当前区间和等于左右节点的区间和
}//线段树的建立
那么如果我们想修改一个线段树的节点或者区间该怎么做呢?我们首先要找到对应区间的叶节点,更改其叶节点后再依次对其父节点进行修改:
void update(int now,int left,int right)//left和right代表需要修改区间的左右端点
{
if(s[now].right<left||s[now].left>right||s[now].mark==1)
{
return;
}//区间越界,终止递归
if(s[now].left==s[now].right)
{
cin>>s[now].value;//找到叶节点并进行更改
return;
}
else
{
update(2*now,left,right);
update(2*now+1,left,right);
s[now].value =s[now*2].value+s[now*2+1].value;//当前区间和等于左右节点的区间和
}
}
最后的最后,如果我们想要查找线段树的某一区间应该怎么做?
对于本例而言,只需找到区间在线段树的小区间即可(答案存在ans变量里哦):
void find(int now,int left, int right)
{
if(s[now].left>right||s[now].right<left)
{
return;
}//完全不在区间内,不管了
if(s[now].left>=left &&s[now].right<=right)//完全在区间里,放心加
{
ans+=s[now].value;//将值加到ans上去,下面的就不用找了
return;
}
else
{
find(2*now,mark);
find(2*now+1,mark);
}//递归查找
}
以上就是本萌新带来的线段树的相关知识,希望对大家有所帮助。
最后的最后,附上一道例题,区间最小值小于十为线段树的标记。
样例:输入 #1
3
5 8
1 420 69 1434 2023
1 2 3
2 2
2 3
2 4
1 2 5
2 1
2 3
2 5
2 3
9999 1000
1 1 2
2 1
2 2
1 1
1
2 1
输出#1:
6
15
1434
1
6
7
36
1
1
再附上一手题目链接:https://www.luogu.com.cn/problem/CF1791F
代码:
#include<iostream>
#include<algorithm>
using namespace std;
struct tree
{
int left,right,value,mark;
}s[800001];
void build(int now,int left,int right)
{
s[now].left=left,s[now].right=right,s[now].mark=0;
if(left==right)
{
cin>>s[now].value;
if(s[now].value<10)
{
s[now].mark=1;
}
return;
}
int mid=(left+right)/2;
build(now*2,left,mid);
build(now*2+1,mid+1,right);
s[now].mark=s[now*2].mark&&s[now*2+1].mark;
}//线段树的建立
void change(int node)
{
int temp=s[node].value;int ans=0;
while(temp>0)
{
ans+=(temp%10);
temp/=10;
}
s[node].value=ans;
if(ans<10)
{
s[node].mark=1;
}
}//线段树的改变
void update(int now,int left,int right)
{
if(s[now].right<left||s[now].left>right||s[now].mark==1)
{
return;
}
if(s[now].left==s[now].right)
{
//cout<<"&&& "<<s[now].left<<" "<<s[now].right<<endl;
change(now);
return;
}
else
{
update(2*now,left,right);
update(2*now+1,left,right);
s[now].mark=s[2*now].mark&&s[2*now+1].mark;
}
}
void find(int now,int mark)
{
if(s[now].left>mark||s[now].right<mark)
{
return;
}
if(s[now].left==s[now].right&&s[now].left==mark)
{
cout<<s[now].value<<endl;
return;
}
else
{
find(2*now,mark);
find(2*now+1,mark);
}
}
int main()
{
int t;
cin>>t;
while(t--)
{
int n,q;
cin>>n>>q;
build(1,1,n);
for(int i=0;i<q;i++)
{
int temp1=-1,temp2=-1,temp3=-1;
cin>>temp1;
if(temp1==1)
{
cin>>temp2>>temp3;
update(1,temp2,temp3);
}
if(temp1==2)
{
cin>>temp2;
find(1,temp2);
}
}
}
}