线段树入门基础(转)

转自:http://blog.csdn.net/libin56842/article/details/8530197

风格:

maxn是题目给的最大区间,而节点数要开4倍,确切的说……

lson和rson辨别表示结点的左孩子和右孩子。

PushUp(int rt)是把当前结点的信息更新到父节点

PushDown(int rt)是把当前结点的信息更新给孩子结点。

rt表示当前子树的根(root),也就是当前所在的结点。


思想:

对于每个非叶节点所标示的结点 [a,b],其做孩子表示的区间是[a,(a+b)/2],其右孩子表示[(a+b)/2,b].

构造:



离散化和线段树:

题目:x轴上有若干个线段,求线段覆盖的总长度。

普通解法:设置坐标范围[min,max],初始化为0,然后每一段分别染色为1,最后统计1的个数,适用于线段数目少,区间范围小。

离散化的解法:离散化就是一一映射的关系,即将一个大坐标和小坐标进行一一映射,适用于线段数目少,区间范围大。

例如:[10000,22000],[30300,55000],[44000,60000],[55000,60000].

第一步:排序 10000 22000 30300 44000 55000 60000

第二部:编号 1        2        3         4       5         6

第三部:用编号来代替原数,即小数代大数 。

[10000,22000]~[1,2]

[30300,55000]~[3,5]

[44000,60000]~[4,6]

[55000,60000]~[5,6]

然后再用小数进行普通解法的步骤,最后代换回去。

线段树的解法:线段树通过建立线段,将原来染色O(n)的复杂度减小到 log(n),适用于线段数目多,区间范围小的情况。

离散化的线段树:适用于线段数目多,区间范围大的情况。


构造:

动态数据结构

struct node{

 node* left;

 node* right;

……

}

静态全局数组模拟(完全二叉树):

struct node{

  int left;

  int right;

……

}Tree[MAXN]

例如:



线段树与点树:

线段树的每一个结点表示一个点,成为点树,比如说用于求第k小数的线段树。

点树结构体:

struct node{

int l, r;

int c;//用于存放次结点的值,默认为0

}T[3*MAXN];

创建:

创建顺序为先序遍历,即先构造根节点,再构造左孩子,再构造右孩子。

[cpp]  view plain copy
  1. void construct(int l, int r, int k){  
  2.     T[k].l = l;  
  3.     T[k].r = r;  
  4.     T[k].c = 0;  
  5.     if(l == r) return ;  
  6.     int m = (l + r) >> 1;  
  7.     construct(l, m, k << 1);  
  8.     construct(m + 1, r, (k << 1) + 1);  
  9.     return ;  
  10. }  
[cpp]  view plain  copy
  1. void construct(int l, int r, int k){  
  2.     T[k].l = l;  
  3.     T[k].r = r;  
  4.     T[k].c = 0;  
  5.     if(l == r) return ;  
  6.     int m = (l + r) >> 1;  
  7.     construct(l, m, k << 1);  
  8.     construct(m + 1, r, (k << 1) + 1);  
  9.     return ;  
  10. }  
 


[A,B,C]:A表示左值,B表示右值,C表示在静态数组中的位置,由此可知,n个点的话大约共有2*n个结点,因此开3*n的结构体一定是够的。


更新值:

[cpp]  view plain copy
  1. void insert(int d, int k){  
  2.     //如果找到了就c值+1返回。   
  3.     if(T[k].l == T[k].r && d == T[k].l){  
  4.         T[k].c += 1;  
  5.         return ;  
  6.     }  
  7.     int m = (T[k].l + T[k].r) >> 1;  
  8.     if(d <= m) insert(d, k << 1);  
  9.     else insert(d, (k << 1) + 1);  
  10.     //更新每一个c,向上更新   
  11.     T[k].c = T[k << 1].c + T[(k << 1) + 1].c;  
  12. }  
[cpp]  view plain  copy
  1. void insert(int d, int k){  
  2.     //如果找到了就c值+1返回。  
  3.     if(T[k].l == T[k].r && d == T[k].l){  
  4.         T[k].c += 1;  
  5.         return ;  
  6.     }  
  7.     int m = (T[k].l + T[k].r) >> 1;  
  8.     if(d <= m) insert(d, k << 1);  
  9.     else insert(d, (k << 1) + 1);  
  10.     //更新每一个c,向上更新  
  11.     T[k].c = T[k << 1].c + T[(k << 1) + 1].c;  
  12. }  

查找值:

[cpp]  view plain copy
  1. //k表示树根,d表示要查找的值   
  2. void search(int d, int k, int& ans)  
  3. {  
  4.     if(T[k].l == T[k].r){  
  5.         ans = T[k].l;  
  6.         ans = T[k].l;  
  7.     }  
  8.     int m = (T[k].l + T[k].r) >> 1;  
  9.     //不懂   
  10.     if(d > T[(k << 1)].c) search(d - T[k << 1].c, (k << 1) + 1, ans);  
  11.     else search(d, k << 1, ans);  
  12. }  
[cpp]  view plain  copy
  1. //k表示树根,d表示要查找的值  
  2. void search(int d, int k, int& ans)  
  3. {  
  4.     if(T[k].l == T[k].r){  
  5.         ans = T[k].l;  
  6.         ans = T[k].l;  
  7.     }  
  8.     int m = (T[k].l + T[k].r) >> 1;  
  9.     //不懂  
  10.     if(d > T[(k << 1)].c) search(d - T[k << 1].c, (k << 1) + 1, ans);  
  11.     else search(d, k << 1, ans);  
  12. }  

search函数的用法不太懂。

例题解:

(待更新)


四类题型:

1.单点更新   只更新叶子结点,然后把信息用PushUp(int r)这个函数更新上来。

hdu1166:敌兵布阵

线段树功能:update:单点替换 query:区间最值




poj2828

树状数组:

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值