线段树入门练习(单点更新,区间修改)长期更新

本文集合转载自本人博客leen.zw,部分内容参考bilibiliUP主@Nemaleswang

HDU-1754

KEY:单点更新并询问区间最大值

#include<bits/stdc++.h>
#define MAXN 200010
using namespace std;
const int  MAXNODE = 1<<19;
struct node{
    int v;
    int left,right;
}node[MAXNODE];
int f[MAXN];//保存每单个节点的对应下标 
void BuildTree(int i,int left,int right){//区间[left,right]建立以i为祖先线段树,i为数组下标即为结点序号
    node[i].left = left;//写入第i个节点中的左区间
    node[i].right = right;//写入第i个节点中的右区间
    node[i].v = 0;//每个区间初始化为0
    if(left == right){//区间长度为0时,结束递归
        f[left] = i;//记录两边相同(即点)对应的序号,为了更新时从下到顶
        return;
    }
    /*将区间一分为二*/
    BuildTree(i<<1,left,(left+right)/2.0);//往左孩子方向继续建立线段树
    BuildTree((i<<1)+1,(left+right)/2.0+1,right);//往右
}
void UpdateTree(int ri){//从下往上更新
    if(ri == 1)return;//向上找到了祖先(整个线段树的祖先,对应下标为1)
    int fi = ri/2.0;//fi为ri的父节点
    int a = node[fi<<1].v;//fi的左儿子
    int b = node[(fi<<1)+1].v;//fi的右儿子
    node[fi].v = max(a,b);//更新fi区间的最大值(将左右儿子的最大值进行比较处理即为fi区间最大值)
    UpdateTree(ri/2);//继续递归向上
} 
int Max;
void Query(int i,int l,int r){//寻找l到r的最大值
    if(node[i].left == l&&node[i].right == r){//找到了一个完全重合的区间
        Max = max(Max,node[i].v);//直接比较
        return; 
    }
    i = i<<1;//向下找
    if(l <= node[i].right)//左区间有涉及
        if(r <= node[i].right)Query(i,l,r);//全包含在左区间内,形态不变
        else Query(i,l,node[i].right);//半包于左区间,则需将区间拆分,左端点不变,右端点变成左孩子的右区间端点
    i++;
    if(r >= node[i].left) //与上相反
        if(l >= node[i].left)Query(i,l,r);
        else Query (i,node[i].left,r);
}
int main(){
    int n,m,g;
    while(scanf("%d%d",&n,&m)!=EOF){
        BuildTree(1,1,n);
        char ch;
        for(int i = 1;i <= n;i++){
            scanf("%d",&g);
            node[f[i]].v = g;
            UpdateTree(f[i]);
        } 
        int a,b;
        while(m--){
            getchar();
            scanf("%c%d%d",&ch,&a,&b);
            if(ch == 'Q'){
                Max = 0;
                Query(1,a,b);
                printf("%d\n",Max);
            }
            else{
                node[f[a]].v  = b;
                UpdateTree(f[a]);
            }
        }
    }
    return 0;
}

Tips:位运算 i<<1为二进制下i左移一位,i>>1反之。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值