虽然学的不多,但是学习线段树的时候,还是让我想起了堆排序,对堆的维护就类似线段树的更新操作。
总之,线段树是一个完全二叉树,利用了二分的思想,大大优化了对于区间的一系列操作
这就是一个最基本的结构,针对这个结构来看一看我们的基础准备
const int max_node = 1 << 19;
const int Max = 2e6 + 10;//针对题目
struct node{
int value;
int l,r;
}node[max_node];
int pre[Max];//每个点(区间长度为0时)对应的结构体数组下标)max_node ->是层数~
value是每一个节点所对应的最大或最小值,依据题目来定
pre[x] 下面用的时候会有注释
开始建一个空树
void build_tree(int i,int l,int r)//为区间[l,r]建立一个以i为祖先的线段树,i为数组下标,我称作结点序号
{
node[i].l = l;
node[i].r = r;
node[i].value = 0;//初始化
if(l == r)
{
pre[l] = i;//能知道某个点对应的序号,为了更新的时候从下往上一直到顶
//记录区间1,n每一个之所对应的节点(都是底层节点)编号
//方便从下到上的更新和查值2
return;
}
build_tree(i * 2,l,(r + l) / 2);
build_tree((i * 2) + 1,(r + l) / 2 + 1,r);
}当l == r时,就说明到了叶要结束了,这时候记录这个叶所对应的编号是几,这样我们可以通过叶索引处底层编号,然后层层修改值进行更新
如果没有到叶,根据二分的思想,层层建树就好
void updatetree(int ri)//换值后的更新
{
if(ri == 1)return;//找到了最高节点结束更新的操作
int fi = ri / 2;
int a = node[fi << 1].value;
int b = node[(fi << 1) + 1].value;
node[fi].value = max(a,b);
updatetree(fi);
}由下往上的更新,这个例子存的是最大值,跟新完一个节点的值后,找到其父节点进行更新,层层向上,知道祖宗节点
void query(int i,int l,int r)
{
if(node[i].l == l && node[i].r == r)//所求区间重合的情况
{
maX = max(maX,node[i].value);
return;
}
i <<= 1;//访问左右区间注意接受他的值!!
if(l <= node[i].r)
{
if(r <= node[i].r)
{
query(i,l,r);
}
else
{
query(i,l,node[i].r);
}
}
i++;
if(r >= node[i].l)
{
if(l >= node[i].l)
{
query(i,l,r);
}
else
{
query(i,node[i].l,r);
}
}
}判断区间进行查值,如果恰好重合那就return,否则继续二分下去,对于跨区间的查询,再查询过程中,所给区间也会被二分开,然后寻找最大值,最后返回唯一maX
int main()
{
int n,m,g;
ios::sync_with_stdio(false);//关闭cin,cout和stdio的同步加快速度
//达到scanf和printf的速度
while(cin>>n>>m)
{
build_tree(1,1,n);
for(int i = 1;i <= n;i++)
{
cin>>g;
node[pre[i]].value = g;
updatetree(pre[i]);
}
string op;
int a,b;
while(m--)
{
cin>>op>>a>>b;
if(op[0] == 'Q')
{
maX = 0;
query(1,a,b);
cout<<maX<<endl;
}
else
{
node[pre[a]].value = b;
updatetree(pre[a]);
}
}
}
return 0;
}
针对HDU1754的main,都是最基础的线段树的操作,还是挺容易上手的
本文详细介绍了线段树的基本概念及其应用,通过实例演示如何构建和更新线段树,实现区间查询等功能。适合初学者快速掌握线段树的使用。
1302

被折叠的 条评论
为什么被折叠?



