首先,线段树,你细品……
em……
首先,线段树的作用是神马?
就是找一个区间内的最大值。
“那不是有手就行?”
但是,要替换!
就像这道非常BT的1.1.1例题:
在N(1<=N<=100000)个数A1…An组成的序列上进行M(1<=M<=100000)次操作,操作有两种:
(1)1 x y:表示修改A[x]为y;
(1)2 x y:询问x到y之间的最大值。
输入
第一行输入N(1<=N<=100000),表示序列的长度,接下来N行输入原始序列;接下来一行输入M(1<=M<=100000)表示操作的次数,接下来M行,每行为1 x y或2 x y
输出
对于每个操作(2)输出对应的答案。
样例输入
5
1
2
3
4
5
3
2 1 4
1 3 5
2 2 4
样例输出
4
5
还好,但是看看数据范围:
第一行输入N(1<=N<=100000),表示序列的长度,接下来N行输入原始序列;接下来一行输入**M(1<=M<=100000)**表示操作的次数,接下来M行,每行为1 x y或2 x y
还有手就行吗?
所以我们就要学习线段树,在此基础上加上修改操作的算法。
由于我是一名低IQ用户,所以这里就讲解一下最适合新手的做法。
1.建树
线段树就是一棵二叉树,每一个节点存储的是l(left)~r(right)里的最大值。
首先,线段树的存储方式肯定得是一维数组,所以我们就可以通过一位数组的每一个下标求出当前节点的l和r,直至l=r,然后我们就令:
if(l==r){
a[i].maxn=b[l];//a是线段树,b是我们输入的数组
return ;
}
然后其他的操作自然就是大名鼎鼎,宇宙无敌,举世无双,超级无敌牛逼的
二分查找!
别再问我二分查找是什么,快速排序总知道吧…
然而我们该怎么确定一个不是最底层节点的最大值呢?
仔细观察然后细品,然后就发现:
父亲节点=两个儿子节点的最大值!
a[i].maxn=max(a[2*i].maxn,a[2*i+1].maxn);
所以这里我们就完成了建树部分:
建树部分代码
//a数组存储的是线段树,b数组是输入的数组。
void make(int l,int r,int i){
if(l==r){
a[i].maxn=b[l];
return ;
}
int m=(l+r)/2;
make(l,m,2*i);
make(m+1,r,2*i+1);
a[i].maxn=max(a