线段树——持续更新辣

一:线段树的基本概念

1:概述

线段树,类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,基本能保持每个操作的复杂度为O(log n)
性质:假设某节点对应的区间是[a,b],设c=(a+b)/2,则左子树对应的区间是[a,c],右子树对应的区间是[c+1,b],线段树空间复杂度为O(n);

2:维护点及点修改的线段树

(1)构造线段树

函数:build(int begin,int end,int node) //构造一个线段树OwO

每个节点维护区间内最小值

主要思想是递归构造,如果当前节点记录的区间只有一个值,则直接赋值,否则递归构造左右子树,最后把左右较小者赋给当前的节点

int array[MAX_N];
int segtree[4*MAX_N+10];
void build(int node, int begin, int end)
{
    if (begin == end)
        segTree[node] = array[begin]; /* 只有一个元素,节点记录该单元素 */
    else
    {
        /* 递归构造左右子树 */
        build(2*node, begin, (begin+end)/2);
        build(2*node+1, (begin+end)/2+1, end);   

        /* 回溯时得到当前node节点的线段信息 */
        if (segTree[2 * node] <= segTree[2 * node + 1])
            segTree[node] = segTree[2 * node];
        else
            segTree[node] = segTree[2 * node + 1];
    }
}  

(2)查询最小值

函数:Query(int begin,int end,int node,int left,int right) //查询[begin,end]的最小值,后面3个参数是在递归时为了计算方便而使用的.调用的时候用填0,0,n;

//如:我要查询[600,6000]的最小值,就用Query(600,6000,0,0,n);

主要思想是把所要查询的区间[a,b]划分为线段树上的节点,然后将这些节点代表的区间合并起来得到所需信息

int Query(int begin,int end,int node,int left,int right)//查询[begin,end]的最小值
{
    int leftQ, rightQ;
    //如果完全不相交,则返回一个充分大的数INF,这里推荐用INT_MAX
    if (left > end || right < begin)  return INF;
    /*  如果完全包含  */
    /*  返回当前节点的值  */
    if (begin >= left && end <= right)
        return segTree[node];    

    /*  比较左子树和右子树的最小值  */
    leftQ = Query(begin,(begin+end)/2,2*node,left,right);
    rightQ = Query((begin+end)/2+1,end,2*node+1,left,right);    

    /*  返回这个值  */
    return min(leftQ,rightQ);
}  

(3)单节点更新

函数:void Updata(int begin, int end,int node, int ind, int add) //把编号为node的代表[begin,end]区间的节点加上add

void Updata(int begin,int end,int node,int ind,int add)/*单节点更新*/
{
    //如果当前是叶子节点,则直接更新
    if(begin==end)
    {
        segTree[node]+=add;
        return;
    }
    int m=(left+right)>>1;  //‘>>1‘相当于除以2
    if(ind<=m)Updata(left,m,node*2,ind,add);    //二分搜索
    else Updata(m+1,right,node*2+1,ind,add);    //同样的二分搜索
    /*更新父节点*/
    segTree[node]=min(segTree[node*2],segTree[node*2+1]);     

}   

3.维护区间及区间修改的线段树

(1)概述

给出一个n个元素的数组A1,A2,...,An,你的任务是设计一个数据结构,支持以下两种操作。

Add(L,R,v).把AL,AL+1,...,AR的值全部增加v。

Query(L,R):计算子序列AL,AL+1,...,AR的元素和、最小值和最大值。

(2)构造线段树

函数:void maintain(int o,int L,int R) //构造一个节点O,对应[L,R]区间

int minv[MAX_N],maxv[MAX_N],sumv[MAX_N],addv[MAX_N];
void maintain(int o,int L,int R)
{
     int lc=o*2,rc=o*2+1; //左子树OvO和右子树OwO
     if(R>L)
     {
         sumv[o]=sumv[lc]+sumv[rc];//考虑左右子树
         minv[o]=min(minv[lc],minv[rc]);
         maxv[o]=max(maxv[lc],maxv[rc]);
     }
     minv[o]+=addv[o];//考虑add操作
     maxv[o]+=addv[o];
     sumv[o]+=addv[o]*(R-L+1);
}

(3)操作1.add

函数:void update(int o,int L,int R) //add操作

递归访问到的结点全部要调用maintain函数更新

void update(int o,int L,int R)  //add操作
{
     int lc=o*2,rc=o*2+1;
     if(yl<=L && y2>=R) addv[o]+=v;//递归边界,累加边界的add值
     else
     {
         int M=L+(R-L)/2;
         if(yl<=M) update(lc,L,M);
         if(y2>M)  update(rc,M+1,R);
     }
     maintain(o,L,R); //递归结束前重新计算本结点的附加信息
}

(4)操作2.Query

函数:void query(int o,int L,int R,int add)

把查询区间递归分解为若干不相交子区间,把各个子区间的查询结果加以合并,但需要注意的是每个边界区间的结果不能直接用,还得考虑祖先结点对它的影响。为了方便,我们在递归查询函数中增加一个参数,表示当前区间的所有祖先结点add值之和

int _min,_max,_sum;       //全局变量,目前位置的最小值、最大值和累加和
void query(int o,int L,int R,int add)
{
     if(yl<=L && y2>=R)//递归边界:用边界区间的附加信息更新答案
     {
         _sum+=sumv[o]+add*(R-L+1);
         _min=min(_min,minv[o]+add);
         _ max=max(_max,maxv[o]+add);
     }
     else  //递归统计,累加参数add
     {
         int M=L+(R-L)/2;
         if(yl<=M) query(o*2,L,M,add+addv[o]);
         if(y2>M)  query(o*2+1,M+l,R,add+addv[o]);
     }
}

未完待续。。。

时间: 2024-11-05 13:39:39

线段树——持续更新辣的相关文章

HDU 1689 Just a Hook 线段树区间更新求和

点击打开链接 Just a Hook Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 18894    Accepted Submission(s): 9483 Problem Description In the game of DotA, Pudge's meat hook is actually the most horrible

POJ 2528 Mayor&#39;s posters (线段树区间更新+离散化)

题目链接:http://poj.org/problem?id=2528 给你n块木板,每块木板有起始和终点,按顺序放置,问最终能看到几块木板. 很明显的线段树区间更新问题,每次放置木板就更新区间里的值.由于l和r范围比较大,内存就不够了,所以就用离散化的技巧 比如将1 4化为1 2,范围缩小,但是不影响答案. 写了这题之后对区间更新的理解有点加深了,重点在覆盖的理解(更新左右两个孩子节点,然后值清空),还是要多做做题目. 1 #include <iostream> 2 #include <

Hdu 3966 Aragorn&#39;s Story (树链剖分 + 线段树区间更新)

题目链接: Hdu 3966 Aragorn's Story 题目描述: 给出一个树,每个节点都有一个权值,有三种操作: 1:( I, i, j, x ) 从i到j的路径上经过的节点全部都加上x: 2:( D, i, j, x ) 从i到j的路径上经过的节点全部都减去x: 3:(Q, x) 查询节点x的权值为多少? 解题思路: 可以用树链剖分对节点进行hash,然后用线段树维护(修改,查询),数据范围比较大,要对线段树进行区间更新 1 #include <cstdio> 2 #include

HDU 5023 A Corrupt Mayor&#39;s Performance Art 线段树区间更新+状态压缩

Link:  http://acm.hdu.edu.cn/showproblem.php?pid=5023 1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #include <vector> 6 #include <string> 7 #include <cmath> 8 using namesp

hihocoder 1080 线段树(区间更新)

题目链接:http://hihocoder.com/problemset/problem/1080 , 两种操作的线段树(区间更新). 这道题前一段时间一直卡着我,当时也是基础不扎实做不出来,今天又想了想其实还是比较简单的,也只能怪自己太弱了. 这道题坑就坑在是有两个操作:set和add,所以lazy标记数组就需要两个,但是有一点要考虑的是一个区间上set与add的先后关系——如果对于一个区间set和add标记同时存在,那么应该如果处理:一种情况set在add之前,那么就按照正常顺序来就可以了:

(简单) POJ 3468 A Simple Problem with Integers , 线段树+区间更新。

Description You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval. 题意

HDU 5023 A Corrupt Mayor&#39;s Performance Art(线段树区间更新)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5023 解题报告:一面墙长度为n,有N个单元,每个单元编号从1到n,墙的初始的颜色是2,一共有30种颜色,有两种操作: P a b c  把区间a到b涂成c颜色 Q a b 查询区间a到b的颜色 线段树区间更新,每个节点保存的信息有,存储颜色的c,30种颜色可以压缩到一个int型里面存储,然后还有一个tot,表示这个区间一共有多少种颜色. 对于P操作,依次往下寻找,找要更新的区间,找到要更新的区间之前

POJ 2777 Count Color (线段树区间更新加查询)

Description Chosen Problem Solving and Program design as an optional course, you are required to solve all kinds of problems. Here, we get a new problem. There is a very long board with length L centimeter, L is a positive integer, so we can evenly d

POJ训练计划2828_Buy Tickets(线段树/单点更新)

解题报告 题意: 插队完的顺序. 思路: 倒着处理数据,第i个人占据第j=pos[i]+1个的空位. 线段树维护区间空位信息. #include <iostream> #include <cstdio> #include <cstring> using namespace std; struct node { int x,v; } num[201000]; int sum[1000000],ans[201000]; void cbtree(int rt,int l,in