线段树学习理解

线段树,学自:

入门 :https://blog.csdn.net/zearot/article/details/52280189

详解:https://blog.csdn.net/zearot/article/details/48299459

上面那两个网址写的非常好,讲线段树讲的很透彻,下面也算是我学习时候的笔记吧。

线段树,就是将线段分成很多的区间,构造出一棵树,根据树的父子结点的关系,来记录出各个区间的和,用父节点等于子节点的和然后进行存储,叶子结点所存的就是所给出的数据序列,然后就可以很快捷的求出在给出的序列上的任意区间之和,并且修改一个数据,只需要更新树上从跟到存储这个数据的子节点的路径上所有的结点的值就行,最多修改次数等于层数。所以对于一个区间要既要修改又要求和的题,用线段树比较方便,它修改和求和都很快,当然还有一个是树状数组,树状数组修改和求和也很快,但是范围小,有的题目能用线段树,但是不能用树状数组,关于树状数组,请看这篇文章:http://www.cnblogs.com/hsd-/p/6139376.html

我也只是入门,只能写写基本的,先上一道题以及我的代码http://codevs.cn/problem/1080/ 最基本的区间查询和点修改的题,也是最简单的线段树的功能的应用,就使用上面的入门博客里面的几个函数

 CODEVS 1080题 http://codevs.cn/problem/1080/

 

题目描述:

一行N个方格,开始每个格子里都有一个整数。现在动态地提出一些问题和修改:提问的形式是求某一个特定的子区间[a,b]中所有元素的和;修改的规则是指定某一个格子x,加上或者减去一个特定的值A。现在要求你能对每个提问作出正确的回答。

单点修改,区间查询问题

/*一段区间,给出数据,要求进行询问和点修改功能*/
#include<bits/stdc++.h>
using namespace std;
#define N 100001
int A[N],Sum[N<<2],n;
//PushUp函数更新节点信息
void PushUp(int rt){ Sum[rt] = Sum[rt<<1]+Sum[rt<<1|1]; }
//建立线段树
void Build(int l,int r,int rt)
{
    if(l == r)
    {
        Sum[rt] = A[l];
        return ;
    }
    int m = (l+r) >> 1;//L+R除以2
    //左右递归
    Build(l,m,rt<<1);//rt*2左节点下标
    Build(m+1,r,rt<<1|1);//tr*2+1 右节点下标
    //子节点遍历结束
    PushUp(rt);//处理父节点,父节点存储左右子节点之和
}
//点修改
void Update(int L,int C,int l,int r,int rt)
{//[l,r]表示当前区间,rt是当前节点编号
    if(l==r)//到达叶节点,修改叶节点的值
    {
        Sum[rt]+=C;
        return ;
    }
    int m = (l+r)>>1;
    if(L <= m) //L的位置是在左边还是在右边,让递归往一边走
        Update(L,C,l,m,rt<<1);
    else       
        Update(L,C,m+1,r,rt<<1|1);
    PushUp(rt);//子节点的信息更新了,父节点也要更新
}
int Query(int L,int R,int l,int r,int rt)
{//[L,R]表示要查询的区间,[l,r]表示当前区间,rt:当前节点编号
    if(L <= l && r <= R)
    {//在区间内直接返回
        return Sum[rt];
    }
    int m = (l+r) >> 1;
    //左子区间:[1,m] 右子区间[m+1,r] 求和区间[L,R]  //累加答案
    int ANS = 0;
    if(L <= m)
        ANS += Query(L,R,l,m,rt<<1);//左子区间与[L,R]有重叠,递归
    if(R > m)
        ANS += Query(L,R,m+1,r,rt<<1|1);//右子区间与[L,R]有重叠,递归
    return ANS;
}
int main(void)
{
    scanf("%d",&n);
    for(int i = 1; i <= n; ++i)//从1开始
    {
        scanf("%d",&A[i]);
    }
    Build(1,n,1);
    int m;
    scanf("%d",&m);
    while(m--)
    {
        int a,b,c;
        scanf("%d %d %d",&a,&b,&c);
        if(a == 1)
            Update(b,c,1,n,1);
        if(a == 2)
            printf("%d\n",Query(b,c,1,n,1));
    }

    return 0;
}

CODEVS 1191题http://codevs.cn/problem/1191/

 

题目描述

在一条数轴上有N个点,分别是1~N。一开始所有的点都被染成黑色。接着
我们进行M次操作,第i次操作将[Li,Ri]这些点染成白色。请输出每个操作执行后
剩余黑色点的个数。

区间修改,单点查询问题

 

/************************************************************
 * > File Name: 1191_数轴染色.cpp                        
 * > Author: weigang                                        
 * > Mail: w_wg@qq.com                                     
 * > Created Time: 2018年05月20日 星期日 13时50分59秒    
 ************************************************************/

#include<bits/stdc++.h>
using namespace std;
int sum[800003];
int lazy[800003];
//建立线段树
void Build(int l, int r,int rt)
{
    if(l == r)
    {
        sum[rt] = 1;
        return ;
    }
    int m = (l+r) >> 1;
    Build(l,m,rt<<1);//rt*2左节点下标
    Build(m+1,r,rt<<1|1);//rt*2+1右结点下标

    sum[rt] = sum[rt<<1]+sum[rt<<1|1];
}
void PushDown(int l,int r,int rt)
{
    if(lazy[rt])
    {
        lazy[rt<<1] = 1;
        lazy[rt<<1|1] = 1;
        sum[rt<<1] = 0;
        sum[rt<<1|1] = 0;
        lazy[rt] =  0;
    }
}
void Update(int L,int R,int l,int r,int rt)
{
    if(L <= l && r <= R) //在要修改的区间内
    {
        sum[rt] = 0;
        lazy[rt] = 1;
        return ;
    }
    int m = (l+r) >> 1;
    PushDown(m-l+1,r-m,rt);
    if(L <= m)     Update(L,R,l,m,rt<<1);
    if(m < R)    Update(L,R,m+1,r,rt<<1|1);

    sum[rt] = sum[rt<<1]+sum[rt<<1|1];
}
int main(void)
{
    int n,m;
    cin >> n >> m;
    Build(1,n,1);
    while(m--)
    {
        int a,b;
        cin >> a >> b;
        Update(a,b,1,n,1);
        cout << sum[1] << endl;
    }

    return 0;
}

 

 

 

 

posted @ 2018-05-19 17:16  凉菜花生小酒  阅读(127)  评论(0)    收藏  举报