线段树的应用

一、最简单的应用就是记录线段有否被覆盖,并随时查询当前被覆盖线段的总长度。那么此时可以在结点结构中加入一个变量int count;代表当前结点代表的子树中被覆盖的线段长度和。这样就要在插入(删除)当中维护这个count值,于是当前的覆盖总值就是根 节点的count值了。
 
二、另外也可以将count换成bool cover;支持查找一个结点或线段是否被覆盖。
 
三、实际上,通过在结点上记录不同的数据,线段树还可以完成很多不同的任务。例如,如果每次插入操作是在一条线段上每个位置均加k,而查询操作是计算一条线段上的总和,那么在结点上需要记录的值为sum。
这里会遇到一个问题:为了使所有sum值都保持正确,每一次插入操作可能要更新O(N)个sum值,从而使时间复杂度退化为O(N)。
 
解决方案是 Lazy思想:对整个点进行的操作,先在结点上做标记,而并非真正执行,直到根据查询操作的需要分成两部分。
根据Lazy思想,我们可以在不代表原线段的结点上增加一个值toadd,即为对这个结点,留待以后执行的插入操作k值的总和。对整个结点插入时,只更新sum和toadd值而不向下进行,这样时间复杂度可证明为O(logN)。
对一个toadd值为0的结点整个进行查询时,直接返回存储在其中的sum值;而若对toadd不为0的一部分进行查询,则要更新其左右子结点的sum值,然后把toadd值传递下去,再对这个查询本身,左右子结点分别 递归下去。时间复杂度也是O(logN)。
模板代码:求逆序数
//模板 线段树求循环逆序数序列最小值  hdu1394
#include<iostream>
#include<stdio.h> 
#define LL(x) ((x)<<1)  //两倍;
#define RR(x) ((x)<<1|1)  //两倍+1;
 
using namespace std;
 
struct Seg_Tree {
        int left,right,val;
        int calmid() {
              return (left+right)/2;
        } 
}tt[15000];
 
int val[5001];
 
void build(int left,int right,int idx) { //idx 序号从1开始
 
          tt[idx].left = left;
          tt[idx].right = right;
          tt[idx].val = 0;
          if(left == right) return; 
          int mid = tt[idx].calmid();
          build(left,mid,LL(idx));
          build(mid+1,right,RR(idx));
}
 
//查询[left,right]中数的个数,不要把left,right看作序号,要看做数;
 
int query(int left,int right,int idx) 
{
          if(left == tt[idx].left && right == tt[idx].right)  
                   return tt[idx].val; 
          int mid = tt[idx].calmid();
          if(right <= mid)  
          { 
             return query(left,right,LL(idx));
          }  
          else if(mid < left) 
          { 
            return query(left,right,RR(idx));
          }  
          else 
          { 
           return query(left,mid,LL(idx)) + query(mid+1,right,RR(idx));
          }
}
 
//更新所有包含id这个数的区间的val值,都+1;
 
void update(int id,int idx) 
{
 
       tt[idx].val ++;
       if(tt[idx].left == tt[idx].right) return; 
       int mid = tt[idx].calmid();
       if(id <= mid)  
       { 
         update(id,LL(idx));
       }
       else  
       { 
         update(id,RR(idx));
       }
}
 
int main() 
{
      int i,n;
      while(scanf("%d",&n) == 1)  
      { 
           build(0,n-1,1);
           //for(i=0;i<n;i++)
          
           int sum = 0;
           for(i=0;i<n;i++)
           {
              scanf("%d",&val[i]);
              //printf("%d\n",query(val[i],n-1,1));
              sum += query(val[i],n-1,1); //1是根
              update(val[i],1);//1是根
           }
           int ret = sum;
           for(i=0;i<n;i++)
           {
                sum = sum - val[i] + (n - val[i] - 1);
                ret=min(ret,sum);
           }
           printf("%d\n",ret);
       }
       return 0;
}

转载于:https://www.cnblogs.com/myth-peng/p/3217939.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值