• 线段树心得


    核心知识点:

    (i)如何构造一颗线段树:

    void build(int rt,int L,int R)

    {

       if(L==R)

        scanf("%d",&Max[rt]);

       else {  

           int M=(L+R)/2;

          build(rt*2,L,M);

           build(rt*2+1,M+1,R);

           Max[rt]=max(Max[rt*2]+Max[rt*2+1]);

          //把孩子的节点信息更新到叶子节点

         }

    } //相当于一个后序遍历。

    首先应当明确,整个过程是由后到前的,也正如学长所给的代码,最后一句画龙点睛的讲到是相当于一个后续遍历。怎么呢?当每个部分不是一个元节点的时候,开始的那个if是没有调用的,直接是执行下面的语句。也就是说,一段长度的线段它内部的最大值是没有确定下来的。到最后当每个孩子节点的值确定后才会通过递归调用的方式返回,慢慢的填满整个树的结构。

    询问:即通过遍历每个在查询范围的小区间,从中选出最大值,ql,qr代表查询的区间

    int query(int r,int L,int R)

    {

    int ans=-1;

    int M=(L+R)/2;

    if(ql<=L&&R<=qr)

    return Max[r]; //区间要全部在查询的区间上(这个是查询的区间比已经有的区间还要大,直接返回已有的最大值)

    if(qr<=M) return query(r*2,L,M); //查询的区间在根节点的左孩子上,进入左孩子查询

    else if(ql>M) return query(r*2+1,M+1,R); //查询的区间在根节点的右孩子上,进入右孩子查询

    else { ans=max(query(r*2,L,M),query(r*2+1,M+1,R));

    }

    //若是查询区间在左孩子的区间和右孩子的区间都有,则进入该区间的左右孩子查询,并返还其最大值 

    //其实你仔细看代码就会发现,真正返回有效值的操作是第一步,但是讨论是必须要分为四个步骤进行的

    更新:即先更新每个叶子节点,区间长度为1的节点。叶子节点更新好后,就把节点信息往上更新,直到根节点。

    void update(int r,int L,int R)

    { int M=(L+R)/2;

    if(L==R) Q[r]=v; ///直到范围长度为1的时候即找到要更新的叶子节点

    else {

    if(p<=M) update(r*2,L,M);

    else update(r*2+1,M+1,R); ///不断缩小搜索范围,与2分有点像。

    Q[r]=max(Q[r*2],Q[r*2+1]); ///叶子节点更新后,开始向上更新。

    }

    }///更新线段树

    //在这里,r的值相当于目前处理的值的下标,注意,他们所有的值都是通过数组进行保存的,整颗二叉树,注意,这个函数中有两处给Q[r]赋值的地方,这也就是直接赋值和递归赋值的两个端口

    /////////////////////////////////////////////////////////

    //下面还是参考上次的模版题——排兵布阵

    #include <iostream>
    #include <stdio.h>
    #include <memory.h>
    using namespace std;

    int n, a[50005];
    char sh[15];

    int lowbit(int i) //树状数组最巧妙之处:i&(-i)
    {
    return i&(-i);
    } //满足2^k<=t的最大的2^k,其中k为非负整数

    void update(int i, int val) //更新函数
    {
    while(i <= n)
    {
    a[i] += val;
    i += lowbit(i);
    }
    }

    int sum(int i) //求和函数
    {
    int sum = 0;
    while(i > 0)
    {
    sum += a[i];
    i -= lowbit(i);
    }
    return sum;
    }

    int main()
    {
    int i, val, t, x, y, zz = 1;
    scanf("%d", &t);
    while(t--)
    {
    memset(a, 0, sizeof(a));
    scanf("%d", &n);
    for(i = 1; i <= n; i++)
    {
    scanf("%d", &val);
    update(i, val); //在实际的运用中是没有创建这个操作的,直接是使用更新这个方式实现的
    }
    printf("Case %d: ", zz++);
    while(scanf("%s", sh))
    {
    if(sh[0] == 'E') break;
    scanf("%d %d", &x, &y);
    if(sh[0] == 'A') update(x, y);
    else if(sh[0] == 'S') update(x, -y);
    else printf("%d ", sum(y)-sum(x-1)); //两段区间和相减
    }
    }

    return 0;
    }

    我要坚持一年,一年后的成功才是我想要的。
  • 相关阅读:
    进程,线程,协程,异步IO知识点
    Socket网络编程知识点
    面向对象编程知识点
    Zabbix系列之七——添加磁盘IO监测
    WARNING: 'aclocal-1.14' is missing on your system.
    tomcat的catalina.out日志按自定义时间日式进行分割
    Plugin with id 'com.novoda.bintray-release' not found.的解决方案
    MaterialCalendarDialog【Material样式的日历对话框】
    导入项目报错【Minimum supported Gradle version is 3.3. Current version is 2.14.1】
    通过Calendar简单解析Date日期,获取年、月、日、星期的数值
  • 原文地址:https://www.cnblogs.com/tianxia2s/p/3879998.html
Copyright © 2020-2023  润新知