CODEVS 1081线段树练习2

#include<stdio.h>
#include<iostream>
using namespace std;
int n,f[400010],q,x,y,z;
struct tree{
    int left,right,value;//每个树节点所表示范围的左l右r和 叶节点的值 或 非叶节点的 flag<加的数> 
}a[400010];
void build(int l,int r,int cur){//构建线段树
    a[cur].left=l;//记录该点的左右起止范围
    a[cur].right=r;
    if(l==r){//如果是叶节点
        f[l]=cur;//f数组记录某个区间内只有一个值<叶节点>的位置<树上的节点位置cur>
        return ;
    }
    build(l,(l+r)/2,cur*2);//递归左子树
    build((l+r)/2+1,r,cur*2+1);//递归右子树
    return ;
}
void add(int l,int r,int cur,int c){//[l,r]区间内加c,从树的根节点i=1 开始搜索
    if(a[cur].left==l&&a[cur].right==r){//找到目标区间,区间内加c
        a[cur].value+=c; return ;
    }
    //下面讨论区间[l,r]与节点i区间的关系,并进行判断递归 
    int mid=(a[cur].left+a[cur].right)/2;//cur点的中点
    if(r<=mid&&l<=mid) add(l,r,cur*2,c);//满足该情况下.此时[l,r]都在i节点区间的左半部分,递归i节点的左子树 
    if(mid+1<=l&&mid+1<=r) add(l,r,(cur*2)+1,c);//满足该情况下.此时[l,r]都在i节点区间的右半部分,递归i节点的右子树 
    if(l<=mid&&mid+1<=r){//满足该情况下.此时[l,r]横跨i节点区间,i节点的左右子树都要递归 
        add(l,mid,cur*2,c);
        add(mid+1,r,(cur*2)+1,c);
    }
}
int ffind(int cur){//从叶节点开始从下往上搜索,累加搜索到的节点的.value的值,ffind(i/2)表示搜索i节点的父节点,直到根节点 
    if(cur==0) return 0;
    return a[cur].value+ffind(cur/2);
}
int main(){
    freopen("1081.in","r",stdin);
    freopen("1081.out","w",stdout);
    scanf("%d",&n);
    build(1,n,1);//构建线段树
    for(int i=1;i<=n;i++){
        scanf("%d",&x);//读入
        a[f[i]].value=x;//每个点递归到最后都会在f内有值
    }
    scanf("%d",&q);
    for(int i=1;i<=q;i++){
        int k=0;
        scanf("%d",&k);
        if(k==1){
            scanf("%d%d%d",&x,&y,&z);
            add(x,y,1,z);//[x,y]区间内加 z 从树的根节点i=1 开始搜索  
        }
        if(k==2){
            scanf("%d",&x);
            printf("%d",ffind(f[x]));//递归以x点在线段树上的节点位置作为开始坐标
        }
    } 
    return 0;   
}

题解:详见注释。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值