线段树一(单点修改)
概述
线段树就是用一棵二叉树维护某一区间内的某一值(最值,和,乘积......),主要有区间查询和区间修改两种操作,区间修改又有自上而下修改和自下而上修改两种,本人更习惯于自上而下修改。
下面是百度百科对线段树的描述
线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点。
使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN)。而未优化的空间复杂度为2N,实际应用时一般还要开4N的数组以免越界,因此有时需要离散化让空间压缩。
单点修改模板(维护区间和)
void build(int le,int ri,int k,int v,int pl)
{ d[k]+=v;
if(le==ri)return;
int mid=(le+ri)>>1;
if(mid>=pl)
build(le,mid,k*2,v,pl);
else
build(mid+1,ri,k*2+1,v,pl);
}
{ d[k]+=v;
if(le==ri)return;
int mid=(le+ri)>>1;
if(mid>=pl)
build(le,mid,k*2,v,pl);
else
build(mid+1,ri,k*2+1,v,pl);
}
区间查询模板(维护区间和)
int visit(int le,int ri,int k,int x,int y)
{ //if(le>y||ri<x)return 0;
//cout<<k<<' '<<d[k]<<endl;
if(le>=x&&y>=ri)return d[k];
int mid=(le+ri)>>1;
int ans=0;
if(mid>=x)ans+=visit(le,mid,k*2,x,y);
if(mid<y){
ans+=visit(mid+1,ri,k*2+1,x,y);
//cout<<mid<<' '<<1<<endl;
}
return ans;
}
int visit(int le,int ri,int k,int x,int y)
{ //if(le>y||ri<x)return 0;
//cout<<k<<' '<<d[k]<<endl;
if(le>=x&&y>=ri)return d[k];
int mid=(le+ri)>>1;
int ans=0;
if(mid>=x)ans+=visit(le,mid,k*2,x,y);
if(mid<y){
ans+=visit(mid+1,ri,k*2+1,x,y);
//cout<<mid<<' '<<1<<endl;
}
return ans;
}
单点修改模板(维护区间最大值)(特别鸣谢计蒜客ls)
void build(int k,int l,int r,int pl,int v)
{ if(l==r){
d[k]=v;
return;
}
int mid=(l + r)>>1;
if(mid>=pl){
build(k*2,l,mid,pl,v);
}
else {
build(k*2+1,mid+1,r,pl,v);
}
d[k]=max(d[k*2],d[k*2+1]);
}
{ if(l==r){
d[k]=v;
return;
}
int mid=(l + r)>>1;
if(mid>=pl){
build(k*2,l,mid,pl,v);
}
else {
build(k*2+1,mid+1,r,pl,v);
}
d[k]=max(d[k*2],d[k*2+1]);
}
区间查询模板(维护区间最大值)
int visit(int le,int ri,int k,int x,int y)
{ if(le>=x&&ri<=y){
return d[k];
}
int mid=(le+ri)>>1;
int maxn=-1;
if(mid>=x)maxn=max(maxn,visit(le,mid,k*2,x,y));
if(mid<y)maxn=max(maxn,visit(mid+1,ri,k*2+1,x,y));
return maxn;
}
{ if(le>=x&&ri<=y){
return d[k];
}
int mid=(le+ri)>>1;
int maxn=-1;
if(mid>=x)maxn=max(maxn,visit(le,mid,k*2,x,y));
if(mid<y)maxn=max(maxn,visit(mid+1,ri,k*2+1,x,y));
return maxn;
}
例题
有一种神奇斑点蛇,蛇如其名,全身都是斑点,斑点数量可以任意改变。
有一天,蒜头君十分的无聊,开始数蛇上的斑点。假设这条蛇的长度是 Ncm,蒜头君已经数完开始时蛇身的第 icm 上有 ai个i 个斑点。
现在蒜头君想知道这条斑点蛇的任意区间的蛇身上一共有多少个斑点。这好像是一个很容易的事情,但是这条蛇好像是和蒜头君过不去,总是刻意的改变蛇身上的斑点数量。
于是,蒜头君受不了了,加上蒜头君有密集型恐惧症。聪明又能干的你能帮帮他吗?
输入格式
第一行一个正整数 N(N≤50000)表示这条斑点蛇长度为 N 厘米,接下来有 N 个正整数,第 i 个正整数 aii 代表第 iii 个斑点蛇第 iii 厘米开始时有 aia_iai 个斑点(1≤ai≤501\leq a_i\leq 501≤ai≤50)。
接下来每行有一条命令,命令有 4 种形式:
(1) Add i j,i 和 j为正整数,表示第 i 厘米增加 j 个斑点(j 不超过 30);
(2) Sub i j,i 和 j 为正整数,表示第 i厘米减少 j 个斑点(j 不超过 30);
(3) Query i,j,i 和 j 为正整数,i≤j,表示询问第 i 到第 j 厘米的斑点总数(包括第 i 厘米和第 j 厘米);
(4) End 表示结束,这条命令在每组数据最后出现;
最多有 40000 条命令。
输出格式
对于每个 Query 询问,输出一个整数并回车,表示询问的段中的总斑点数,这个数保证在int
范围内。
样例输入
10
1 2 3 4 5 6 7 8 9 10
Query 1 3
Add 3 6
Query 2 7
Sub 10 2
Add 6 3
Query 3 10
End
样例输出
6 33 59
分析
裸的线段树,代码吗......,看模板吧