acwing----线段树

一.线段树的作用:

 待补充:目前我遇到的作用是能够动态的得到某一区间的某一特性。

              什么叫动态呢?

               即我可以临时改变某一区间的值,同时整个区间的这个特性也会动态的改变,保持这个特性;

               如果说前缀和,单调栈等是预处理,静态的,那么这个就是动态的。

二.线段树的实现:

其实现的几个方法:建,查,改。

以具体的例子说明:

/-----------------------------------------------------------------------------------------------------------------/

每个测试的第一行,有两个正整数 N 和 M ( 0<N<=200000,0<M<5000 ),分别代表学生的数目和操作的数目。
学生ID编号分别从1编到N。
第二行包含N个整数,代表这N个学生的初始成绩,其中第i个数代表ID为i的学生的成绩。
接下来有M行。每一行有一个字符 C (只取'Q'或'U') ,和两个正整数A,B。
当C为'Q'的时候,表示这是一条询问操作,它询问ID从A到B(包括A,B)的学生当中,成绩最高的是多少。
当C为'U'的时候,表示这是一条更新操作,要求把ID为A的学生的成绩更改为B。

对于每一次询问操作,在一行里面输出最高成绩。

/-----------------------------------------------------------------------------------------------------------------/

 1.首先要建树:

     

 1 //我们以数组的数据结构建立,以堆的方式去管理,传入的u是要建树的大小,即
 2 //题目所给的某个东西从1——N开始编号的N。
 3 //实际上整个用来保存的数组的大小要开到最大的N的四倍:
 4 struct Node{
 5     int l,r;
 6     int v;//代表所要保存的权重;
 7 }tr[N*4];
 8 void build (int u,int l,int r)
 9 {
10     if (l==r) return ;
11     tr[u]={l,r};
12     int mid=l+r>>1;
13     build(u<<1,l,mid),build(u<<1|1,mid+1,r);//u<<1|1其实是u*2+1的高级写法;
14 }

 现在来解释以下为啥要开到N*4: 由于堆(二叉树)的性质,在除最下面一层,上面一定都是满二叉树,而我假设有h层,在第h-1层有n个节点,那么从第一层开始到h-1层有:

  1+2+4+,,,,+2^(k-1)+2^k 个节点(2^k=n),即 2n-1个节点, 第h层其最多有2*n个节点,那么这个树最多有 4*n-1个节点,而巧了:

   第h-1层是我们放区间 l==r 时的节点,即最初是的数据,有N个;

 

2.再是查:

 1 int query(int u, int l, int r) // u表示现在树上是在那个节点上查,l与r代表查找的范围
 2 {
 3     if (l <= tr[u].l && r >= tr[u].r)
 4         return tr[u].v;
 5     //比如说我要查询(6,10)这个区间的最大值,而现在我在的节点的区间所记录的范围是(6,8),明显我没有在往下分半递归的必要了
 6     //这里我已经找到了(6,8)这个区间的最大值,/*  if (l==r) return tr[u].v; */这个情况被包含到这里了
 7     int mid = tr[u].l + tr[u].r >> 1;
 8     int v = 0;
 9     if (l <= mid)
10         v = query(u << 1, l, r);
11     if (r > mid)
12         v = max(v, quert(u << 1 | 1, l, r));
13     //为什么这里传入的l,r不变呢,这是跟整个query算法设计有关,
14     //这样确实能够保证每个区间都会以if (l<=tr[u].l && r>=tr[u].r) return tr[u].v;的方式返回;
15     return v;
16 }

3.接下来是改:

 1 void modify(int u, int x, int v) // x代表是在哪一个叶子节点上,v代表是改成什么值
 2 {
 3     if (tr[u].l == x && tr[u].r == x)
 4         tr[u].v = v;
 5     else
 6     {
 7         int mid = tr[u].l + tr[u].r >> 1;
 8         if (x <= mid)
 9             modify(u << 1, x, v);
10         else
11             modify(u << 1 | 1, x, v);
12         //每一次子节点改变完了,父节点也要跟着改变:
13         pushup(u);
14     }
15     return;
16 }
17 void pushup(int u)//子节点的改变也要使父节点改变:
18 {
19     tr[u].v = max(tr[u << 1].v, tr[u << 1 | 1].v);
20     return ;
21 }

三.线段树时间复杂度的分析:

 每一次的搜索,改变,只要在O(logn)的时间内可完成

四.待补充:

 

posted @ 2022-04-23 21:57  次林梦叶  阅读(45)  评论(0)    收藏  举报