zoukankan      html  css  js  c++  java
  • 线段树初步

    Segment Tree

    线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。

    功能:单点、区间的修改、查询

    建树

    建树开N的4倍空间(注意,开2倍会访问无效内存)

    为什么呢?我们看一看下面这张图:

    倍数= N(max)/No    -->No也相当于区间的长度。
    N(max)可以看做是一个满二叉树(最好的情况)。N(min)可以看做最后一层只有两个子节点的树(最坏的情况)。
    const int N = 100000;
    
    struct node
    {
        int l,r,v,f;
    }tree[4 * N + 50];// 开4倍N的空间  
    
    long long int sum = 0;
    
    void build(int l,int r,int rt)
    {
        // 初始化 l、r 
        tree[rt].l = l;
        tree[rt].r = r;
        if(l == r)
        {
            scanf("%d",&tree[rt].v);
            return;
        }
        
        int mid = l + r >> 1;
        build(l,mid,rt << 1);
        build(mid + 1,r,rt << 1 | 1);
        
        // 更新 v 
        tree[rt].v = tree[rt << 1].v + tree[rt << 1 | 1].v;
        return;
    }
    
    

    下传懒标记

    void passdown(int k)
    {
        // 下传懒标记 
        tree[k << 1].f += tree[k].f;
        tree[k << 1 | 1].f += tree[k].f;
        // 更新 v 
        tree[k << 1].v += tree[k].f * (tree[k << 1].r - tree[k << 1].l + 1);
        tree[k << 1 | 1].v += tree[k].f * (tree[k << 1 | 1].r - tree[k << 1 | 1].l + 1);
        // 还原根节点的懒标记 
        tree[k].f = 0;
    }

    查询

    void query(int L,int R,int rt)
    {
        int l = tree[rt].l,r = tree[rt].r;
        
        if(L <= l && R >= r)//如果当前区间为目标区间的子区间 
        {
            sum += tree[rt].v;
            return;
        }
        if(tree[rt].f)passdown(rt);//如果有懒标记要及时下传更新 
        int mid = l + r >> 1;
        // 二分寻找目标区间的子区间 
        if(L <= mid) query(L,R,rt << 1);
        if(R > mid) query(L,R,rt << 1 | 1);
    }

    区间更新

    void update(int L,int R,int rt,int add)
    {
        int l = tree[rt].l,r = tree[rt].r;
        
        if(L <= l && R >= r)
        {
            tree[rt].v += add * (r - l + 1);// v 加上该区间中所有元素要加上的总值 
            tree[rt].f += add;// 更新 懒标记 
            return;
        }
        
        if(tree[rt].f) passdown(rt)// 扫下一层之前必须下传懒标记                                                                                                                                                                                                            ;
        int mid = l + r >> 1;
        if(L <= mid) update(L,R,rt << 1,add);
        if(R > mid) update(L,R,rt << 1 | 1,add);
        
        tree[rt].v = tree[rt << 1].v + tree[rt << 1 | 1].v;    // 更新 v 
    }

    (R - L)不是(L - R)!就因为这个手残我卡了半个小时

    模板题

    POJ - 3468 

    进阶题

    (从求区间和变为求最大值)

    P4513 HDU - 4630 HDU - 5726 HDU - 1166 HDU - 1754 

     

    不用下传的版本:

    void update(int L,int R,int val,int l,int r,int rt)
    {
        tree[rt].v += (ll)(R-L+1) * val;
        if(L == l && R == r)
        {
            tree[rt].f += val;
            return;
        }
        int m = l + r >> 1;
        if(R <= m )
            update(L,R,val,l,m,rt << 1);
        else if(L > m)
            update(L,R,val,m+1,r,rt << 1 | 1);
        else update(L,m,val,l,m,rt << 1),update(m+1,R,val,m+1,r,rt << 1 | 1) 
    } 
    
    int query(int L,int R,int l,int r,int rt,int add)
    {
        if(L == l && R == r)
            return tree[rt].v + add * (r-l + 1);
        int m = l + r >> 1;
        if(R <= m)
            return query(L,R,l,m,rt << 1,add + tree[rt].f);
        else if(L > m)
            return query(L,R,m + 1,r,rt << 1 | 1,add + tree[rt].f);
        else return query(L,m,l,m,rt << 1,add + tree[rt].f) + query(m + 1,R,m + 1,r,rt << 1 | 1,add + tree[rt].f);
    }
  • 相关阅读:
    gc buffer busy/gcs log flush sync与log file sync
    给Oracle年轻的初学者的几点建议
    Android 编程下帧动画在 Activity 启动时自动运行的几种方式
    Android 编程下 Touch 事件的分发和消费机制
    Java 编程下 static 关键字
    Java 编程下 final 关键字
    Android 编程下模拟 HOME 键效果
    Why Are Thread.stop, Thread.suspend, Thread.resume and Runtime.runFinalizersOnExit Deprecated ?
    Extjs4 大型项目目录结构重构
    [转]SQLServer 2008 允许远程连接的配置方法
  • 原文地址:https://www.cnblogs.com/Cindy-Chan/p/11232221.html
Copyright © 2011-2022 走看看