首页 > 学院 > 开发设计 > 正文

线段树入门基础(转)

2019-11-06 08:01:27
字体:
来源:转载
供稿:网友

转自: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 plaincopyvoid construct(int l, int r, int k){      T[k].l = l;      T[k].r = r;      T[k].c = 0;      if(l == r) return ;      int m = (l + r) >> 1;      construct(l, m, k << 1);      construct(m + 1, r, (k << 1) + 1);      return ;  }  [cpp] view plain copyvoid construct(int l, int r, int k){      T[k].l = l;      T[k].r = r;      T[k].c = 0;      if(l == r) return ;      int m = (l + r) >> 1;      construct(l, m, k << 1);      construct(m + 1, r, (k << 1) + 1);      return ;  }   

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

更新值:

[cpp] view plaincopyvoid insert(int d, int k){      //如果找到了就c值+1返回。       if(T[k].l == T[k].r && d == T[k].l){          T[k].c += 1;          return ;      }      int m = (T[k].l + T[k].r) >> 1;      if(d <= m) insert(d, k << 1);      else insert(d, (k << 1) + 1);      //更新每一个c,向上更新       T[k].c = T[k << 1].c + T[(k << 1) + 1].c;  }  [cpp] view plain copyvoid insert(int d, int k){      //如果找到了就c值+1返回。      if(T[k].l == T[k].r && d == T[k].l){          T[k].c += 1;          return ;      }      int m = (T[k].l + T[k].r) >> 1;      if(d <= m) insert(d, k << 1);      else insert(d, (k << 1) + 1);      //更新每一个c,向上更新      T[k].c = T[k << 1].c + T[(k << 1) + 1].c;  }  查找值:

[cpp] view plaincopy//k表示树根,d表示要查找的值   void search(int d, int k, int& ans)  {      if(T[k].l == T[k].r){          ans = T[k].l;          ans = T[k].l;      }      int m = (T[k].l + T[k].r) >> 1;      //不懂       if(d > T[(k << 1)].c) search(d - T[k << 1].c, (k << 1) + 1, ans);      else search(d, k << 1, ans);  }  [cpp] view plain copy//k表示树根,d表示要查找的值  void search(int d, int k, int& ans)  {      if(T[k].l == T[k].r){          ans = T[k].l;          ans = T[k].l;      }      int m = (T[k].l + T[k].r) >> 1;      //不懂      if(d > T[(k << 1)].c) search(d - T[k << 1].c, (k << 1) + 1, ans);      else search(d, k << 1, ans);  }  search函数的用法不太懂。

例题解:

(待更新)

四类题型:

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

hdu1166:敌兵布阵

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

poj2828

树状数组:


上一篇:洛谷 P1489 猫狗大战

下一篇:采药

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表