线段树

线段树时=是完全二叉树,原理啥的就不写了,看这张图片应该能回忆起。

图片来自 

线段树

题目:

ProblemE: 操作格子 
 

 Time Limit: 1    Memory Limit: 128 MB Submit  0 Solved  0

 Submit Status Web Board

Description

有n个格子,从左到右放成一排,编号为1-n。
共有m次操作,有3种操作类型:
1.修改一个格子的权值,
2.求连续一段格子权值和,
3.求连续一段格子的最大值。
对于每个2、3操作输出你所求出的结果。

Input

第一行2个整数n,m。
接下来一行n个整数表示n个格子的初始权值。
接下来m行,每行3个整数p,x,y,p表示操作类型,p=1时表示修改格子x的权值为y,p=2时表示求区间[x,y]内格子权值和,p=3时表示求区间[x,y]内格子最大的权值。

 

Output

有若干行,行数等于p=2或3的操作总数。
每行1个整数,对应了每个p=2或3操作的结果。

Sample Input

4 3
1 2 3 4
2 1 3
1 4 3
3 1 4

 

Sample Output

6
3

 

HINT

数据规模与约定

对于20%的数据n <= 100,m <= 200。

对于50%的数据n <= 5000,m <= 5000。

对于100%的数据1 <= n <= 100000,m <= 100000,0 <= 格子权值 <= 10000。

思路:线段树

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string.h>
#define in(x) scanf("%d",&x)
#define out(x) printf("%d",x)
using namespace std;
typedef long long int ll;
const int maxn=1e5+5;
struct node{
    int l,r,max;
    ll sum;
}a[400010];
void build(int i,int l,int r)//建树 初始化 
{
    int mid=(l+r)>>1;
    a[i].l=l,a[i].r=r;
    a[i].max=0,a[i].sum=0;
    if(l==r) return;//如果左右边距相等则不再构建
    build(2*i,l,mid);//左孩子 范围为l到mid
    build(2*i+1,mid+1,r);//右孩子  范围为mid+1到r 
}
void insert(int i,int v,int value)//往下延申更新sum和max 
{
    a[i].sum+=value;//延申经过的点都加上这个值 
    if(a[i].max<value) a[i].max=value;//更新最大值 
    if(a[i].l==a[i].r) return;//左右边距相等时,不再往下延申()
    int mid=(a[i].l+a[i].r)>>1;
    if(v<=mid){//插入到左孩子 
        insert(2*i,v,value);
    }
    else insert(2*i+1,v,value);//插入到右孩子 
}
void update(int i,int v,int value)//更改 
{
    if(v==a[i].l&&v==a[i].r){//如果左右边距都对应相等
        a[i].sum=value;         // 则找到了对应的value
        a[i].max=value;    
        return;                 
    }
    int mid=(a[i].l+a[i].r)>>1;        
    if(v<=mid){
        update(2*i,v,value);//从左孩子出寻找 
    }
    else update(2*i+1,v,value);//从右孩子中寻找
    a[i].sum=a[2*i].sum+a[2*i+1].sum;//更新区间和
    a[i].max=max(a[2*i].max,a[2*i+1].max);//搜索自上而下,更新自下而上 
}
ll getsum(int i,int l,int r)//求区间和 
{
    if(l==a[i].l&&r==a[i].r) return a[i].sum;//如果左右边距都对应相等,
    int mid=(a[i].l+a[i].r)>>1;                // 则找到了对应的sum
    if(r<=mid){
        return getsum(2*i,l,r);//从左孩子出寻找 
    }
    else if(l>mid){//从右孩子中寻找 
        return getsum(2*i+1,l,r);
    }
    else{
        return getsum(2*i,l,mid)+getsum(2*i+1,mid+1,r);//左右均找 
    }
}
int getmax(int i,int l,int r)
{
    if(l==a[i].l&&r==a[i].r) return a[i].max;//如果左右边距都对应相等,
    int mid=(a[i].l+a[i].r)>>1;                // 则找到了对应的max
    if(r<=mid){
        return getmax(2*i,l,r);//从左孩子出寻找 
    }
    else if(l>mid){//从右孩子中寻找 
        return getmax(2*i+1,l,r);
    }
    else{
        return max(getmax(2*i,l,mid),getmax(2*i+1,mid+1,r));//左右均找 
    }    
}                                
int main()
{
    int n,m,value,a,b,c;
    in(n),in(m);
    build(1,1,n);//每次从1开始往下延申 
    for(int i=1;i<=n;i++){
        in(value);
        insert(1,i,value);//每次依旧是从顶(1)往下延申 
    }
    while(m--){
        in(a),in(b),in(c);
        if(a==1) update(1,b,c);
        else if(a==2) printf("%lld\n",getsum(1,b,c));
        else if(a==3) printf("%d\n",getmax(1,b,c));
    }