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)的时间内可完成
四.待补充: