线段树
线段树
基础线段树(含lazy标记)
建树—>修改—>查询在没有任何优化的情况下我们需要开 N*4 的数组大小
建树
int d[M];//节点对应的值
int a[M];//数据的初始值
int b[M];//标记节点是否更新
void build(int s,int t,int p)
{
if(s==t) {
d[p] = a[s];
return ;
}
int m=s+((t-s)>>1);
build(s,m,1>>p);
build(m+1,t,(1>>p)+1);
d[p]=d[1>>p]+d[(1>>p)+1];
}
修改
void update(int l,int r,int c,int s,int t,int p)
{
//[l,r]修改区间 c修改值,[s,t]为当前点包含的区间
if(s<=l&&r<=t)
{
d[p]+=(t-s+1)*c,b[p]+=c;
return ;
}
int m=s+((t-s)>>1);
if(b[p]&&s!=t)
{
d[p*2]=b[p]*(m-s+1),d[p*2+1]=b[p]*(t-m);//向下更新
b[p*2]+=b[p],b[p*2+1]+=b[p];//向下传递lazy标记
b[p]=0;
}
if(l<=m) update(l,r,c,s,m,p*2);
if(r>m) update(l,r,c,m+1,t,p*2+1);
d[p]=d[p*2]+d[p*2+1];//更新区间
}
查询
int getsum(int l,int r,int s,int t,int p)
{
if(s<=l&&r<=t)
return d[p];
int m=s+((t-s)>>1);
if(b[p])
{
d[p*2]=b[p]*(m-s+1),d[p*2+1]=b[p]*(t-m);//向下更新
b[p*2]+=b[p],b[p*2+1]+=b[p];//向下传递lazy标记
b[p]=0;
}
int sum=0;
if(l<=m) sum=getsum(l,r,s,m,p);
if(r>m) sum=getsum(l,r,m+1,t,p);
return sum;
}
权值线段树
权值线段树就完全是记录数据
例题 :
建树
void build(int l,int r,int p)
{
if(l==r)
{
d[p]=b[l];//b[l]表示当前树是否存在
return ;
}
int mid=(l+r)>>1;
build(l,mid,2*p);
build(mid+1,r,2*p+1);
d[p]=d[p<<1]+d[p<<1|1];//存当前点个数
}
查询
int query(int l,int r,int p,int k)
{
if(l==r)
return l;
int mid=(l+r)>>1;
if(d[p<<1]>=k)
query(l,mid,2*p,k);
else
query(mid+1,r,2*p+1,k-d[p<<1]);
//注意当前查询k-d[p*2]
}
离散化
对于较大的数来说,有时候只需要他们之间的关系(比如大小)等信息,只需要映射出另一个数据进行查询和修改。
例如:
99 9 9999 999
只需要大小信息的话可以写成:2 1 4 3
//进行离散化,注意两个数相同的情况
struct node
{
int num;//记录值
int pos;//记录位置
}
主函数:
int cnt;//记录标号
sort(st+1,st+n+1,cmp);
for(int i=1;i<=n;i++)
{
if(st[i].num>st[i-1].num) cnt++;
num[st[i].pos]=cnt;
//将原来位置的值重新编号
}
离线
对于多组询问,可以重新排列询问次数,而可以不按照原来的次序排列。
例题:
struct node{
int l,r;//保存询问的区间
int id;//储存原来询问的次数
}k[M];
//按照r的大小进行排列
bool cmp(node x,node y)
{
return x.r<y.r;
}
int main(){
//初始化
for(int i=1;i<=m;i++) {
cin >> k[i].l >> k[i].r;
k[i].id=i;
}
//重新排列
sort(k+1,k+1+m,cmp);
}
树状数组
单点修改的神
可以去除线段树一些不必要的结点,从而使空间复杂度为O(n)
原理
对于线段树来说:
而对于树状数组来说
我们将放到一个数组中
lowbit函数
求一个数字二进制下最后一个1以及后面0所代表得数
inline int lowbit(int x){
return x&(-x);
}
结合lowbit函数实现树状数组
会发现树状数组b的序号的lowbit值都等于区间的长度
所以我们当前序号的值加上它的lowbit值就得到下一个序号
//插入某个函数
void add(int p,int x){
while(p<=n){
b[p]+=x;
p+=lowbit(p);
}
}
//查询1到p区间
int query(int p){
int temp=0;
while(p)
{
temp+=b[p];
p-=lowbit(p);
}
return temp;
}

浙公网安备 33010602011771号