• 算法导论学习-线段树(2)


    线段树(1)http://www.cnblogs.com/fu11211129/p/4230000.html

    1. 线段树应用之动态点插与统计:

    --------------------------------

    线段树(1)中讲的应用是区段的插值与统计,我们在线段树结构体中接入cover之一域,cover等于0表示该节点所代表的区域并没有被完全覆盖,cover大于等于1表示该节点所代表区域已经被完全覆盖,用线段树(1)博客里面的图来说明一下:

                      

    如上图所示,我们最后统计的时候是找出cover不为零的节点,(再上图对应的是节点[3,4]. [5,6], [2], [4], [5], [7].),但是注意到[3,4]和[4] 还有[5,6]和[5]不能从重复统计,所以最后统计的长度是[2]+[3,4]+[5,6]+[7]=6.所以四根木条在墙上的总投影长是6. 

    现在我们可以来归纳一下区段插值的几个重要部分:首先是插入动作,比如插入[L, R]线段,插入动作需要递归的从根节点开始插入,而递归的出口就是找到能够和[L, R]完全匹配的线段树节点,即找到a[i].left==L AND a[i].right==R,并且同时将a[i].cover值加1.,递归终止。然后是删除动作,假设删除[L, R],同样的也是要从根节点开始删除,也是要递归的找到与[L, R]完全匹配的线段树节点a[i],并且将a[i].cover值减去1.最后是统计,仍然是从根节点开始递归的统计,递归的出口要么是找到的节点a[i]满足a[i].cover>=0,则返回该节点代表的区段长度。要么是找到叶子节点(a[i].left==a[i].right),如果叶子节点的cover不为0,则返回1,否则返回零。

    以上是对区段插入的回顾,下面开始介绍动态点插与统计。所谓动态点插(其实是我自己DIY的,所以我就用图表示一下吧,大家理解就好)

    如上图所示,1-8表示相互连接的8个槽,然后在这些草内的任意位置放置任意数量个小球,然后让你求出任意区段内[L, R]的小球数量。比如[2,3]区段内的小球数量是2,[1,6]区段内的小球数量是6。要解决这个问题,可以优良中办法,一种方法不用说也知道,暴力嘛,开一个数组A,然后更新数组的值。。这种做法当“槽”的范围过大时是效率很低的。所以这时候线段树再次派上用场了。我们还是用cover域来表示某个节点缩地阿彪范围的小球数量。比如根节点代表1-8槽的小球数量。

    ###第一步,建立线段树(http://www.cnblogs.com/fu11211129/p/4230000.html 里面有介绍,这里直接跳过)

    ###第二步,线段树插入(点插)

    1 void updatePoint(int pos,int step){
    2     a[step].num++;
    3     if(a[step].left==a[step].right&&a[step].left==pos) return;//leaf node
    4     int mid=(a[step].left+a[step].right)/2;
    5     if(pos<=mid) updatePoint(pos,step*2);
    6     else updatePoint(pos,step*2+1);
    7 }

    我们可以看到,线段树点插动作是“一路插值的”,这也不难理解,比如我在第3个槽内放置一个球,那么显然的,节点[3].num=1,但同时的,节点[3,4],[1,2,3,4],[1,2,3,4,5,6,7,8]的num值也是为1.所以线段树点插可以理解记忆为:一路递归向下插值。

    ###第三步,线段树统计

     1 int query(int L,int R,int step){
     2     if(L==a[step].left&&R==a[step].right){
     3         return a[step].num;
     4     }
     5     else{
     6         int mid=(a[step].left+a[step].right)/2,ans=0;
     7         if(R<=mid) ans+=query(L,R,step*2);
     8         else if(L>=mid+1) ans+=query(L,R,step*2+1);
     9         else{
    10             ans+=query(L,mid,step*2)+query(mid+1,R,step*2+1);
    11         }
    12         return ans;
    13     }
    14 }

    可以看到,线段树点插结果统计是按照区段来查询的,所以递归查询的出口也就是找到与之匹配的节点,然后返回相应的cover值,对应代码2-4行。如果与当前节点不匹配,则递归的“往下”查[6-8],如果带查询区段在左右子树都有子结果,则返回两个子结果之和。

    2. 线段树应用之区间最大最小值问题:

    -----------------------------------

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 using namespace std;
     5 const int max_size=100;
     6 const int inf=1<<30;
     7 struct segmentTree{
     8     int lc,rc,mi,mx;
     9 }a[max_size*3];
    10 void buildTree(int L,int R,int rt){
    11     a[rt].lc=L;
    12     a[rt].rc=R;
    13     a[rt].mi=inf;
    14     a[rt].mx=-inf;
    15     if(L==R) return;
    16     int mid=(a[rt].lc+a[rt].rc)/2;
    17     buildTree(L,mid,rt*2);
    18     buildTree(mid+1,R,rt*2+1);
    19 }
    20 void insert(int pos,int v,int rt){
    21     if(pos==a[rt].lc&&a[rt].lc==a[rt].rc){
    22         a[rt].mi=v;
    23         a[rt].mx=v;
    24         return;
    25     } 
    26     int mid=(a[rt].lc+a[rt].rc)/2;
    27     if(pos<=mid) insert(pos,v,rt*2);
    28     else insert(pos,v,rt*2+1);
    29     a[rt].mi=min(a[rt*2].mi,a[rt*2+1].mi);
    30     a[rt].mx=max(a[rt*2].mx,a[rt*2+1].mx);
    31 }
    32 int query_min(int L,int R,int rt){
    33     if(L==a[rt].lc&&R==a[rt].rc) return a[rt].mi;
    34     if(a[rt].lc<a[rt].rc){
    35         int mid=(a[rt].lc+a[rt].rc)/2,ret;
    36         if(R<=mid) ret=query_min(L,R,rt*2);
    37         else if(L>=mid+1) ret=query_min(L,R,rt*2+1);
    38         else ret=min(query_min(L,mid,rt*2),query_min(mid+1,R,rt*2+1));
    39         return ret;
    40     }
    41 }
    42 int main(){
    43     int n,m,q,p,v;
    44     while(scanf("%d",&n)!=EOF){
    45         buildTree(1,n,1);
    46         scanf("%d",&m);
    47         for(int i=1;i<=m;i++){
    48             scanf("%d%d",&p,&v);
    49             insert(p,v,1);
    50         }
    51         scanf("%d",&q);
    52         while(q--){
    53             int a,b;
    54             scanf("%d%d",&a,&b);
    55             printf("%d
    ",query_min(a,b,1));
    56         }
    57     }
    58 }
  • 相关阅读:
    【IBM Tivoli Identity Manager 学习文档】8 Service和Service Type
    【IBM Tivoli Identity Manager 学习文档】13 Service管理
    sql 分组查询中每组中某列的各行字符数据相加显示
    c#压缩和解压缩文件
    三层架构之泛型应用
    Winform下载文件
    [C#]DataTable常用操作总结【转】
    C# 泛型Dictionary (Hashtable)
    C#获取打印机状态
    打印机状态
  • 原文地址:https://www.cnblogs.com/fu11211129/p/4231803.html
Copyright © 2020-2023  润新知