线段树

线段树

基础线段树(含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;
}

权值线段树

权值线段树就完全是记录数据

例题 :

第K小整数 原码

三元上升子序列 原码

建树

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;
        //将原来位置的值重新编号
    }

离线

对于多组询问,可以重新排列询问次数,而可以不按照原来的次序排列。

例题:

HH的项链 原码

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)

原理

对于线段树来说:

image-20230714135154723

而对于树状数组来说

image-20230714135244990

我们将放到一个数组中

image-20230714135859337

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;
}
posted @ 2025-07-07 15:39  星空丶star  阅读(8)  评论(0)    收藏  举报