-————终于从数论中爬出来了,没有继续写数论了
点修改
例题:给出一个有n个元素的数组a1,a2,a3,….an,设计一个数据结构,来支持修改某个元素和计算某个区间的最值
分析:因为要修改元素并且要计算最值,就不能再用ST表和遍历整个区间来计算,这两种方法的时间复杂度都较高,且如果一边修改元素,一边找最值,写出的代码就可以直接拿去卡死评测机.
所以我们要引入线段树(又名区间树)这个新的数据结构,而上述的两种问题的解决恰恰是线段树最优美的地方.
如图所示将n个元素由各个结点来储存,如在[5,7]中就可以储存5,6,7之中的最值.而如果要把7这个结点的值修改,则需要把结点[5,7],[1,7],[1,13]这三个结点的值修改.相比起将数组全部遍历,这种方法简单而高效啊.
#include<cstdio>
using namespace std;
int n; int a[100100]; int minv[101000];
int minx(int xxx,int yyy){
if(xxx<yyy) return xxx;
else return yyy;
}
void init(){
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d",&a[i]);
}
}
void hebin(int k){
minv[k]=minx(minv[k<<1],minv[k<<1|1]);
}
void jianshu(int ql,int qr,int o){
if(ql==qr){
minv[o]=a[ql];
return;
}
int m=ql+(qr-ql)/2;
jianshu(ql,m,o<<1);
jianshu(m+1,qr,o<<1|1);
hebin(o);
}
int main(){
init();
jianshu(1,8,1);
return 0;
}
这是建树的代码,个人比较喜欢用递归实现(像不像快速排序)
下面是先是查询的代码,然后是更新的代码
int ql,qr; //全局变量(查询ql,q2中的最值),这里展示的是最小值
int lookup(int o,int l,int r){ //o表示当前结点编号
int m=l+(r-l)/2,ans=INF; //m这样写是一个小技巧,防止超int(尽量养成这个好习惯)
if(ql<=l && r<=qr) return minv[o];
if(ql<=m) ans=minx(ans,lookup(o*2,ql,m));
if(m<qr) ans=minx(ans,lookup(o*2+1,m+1,qr));
return ans;
}
int p,v;//将a[p]修改为v
void update(int o,int ql,int qr)
{
int m=ql+(qr-ql)/2;
if(ql==qr) minv[o]=v;
else
{
if(p<=m) update(o*2,ql,m);
else update(o*2+1,m+1,qr);
minv[o]=minx(minv[o*2],minv[o*2+1]);
}
}