初识简单数据结构——线段树

洛谷线段树模板
来自互联网

  • 建树和初始化
  • 更新
  • 查找

线段树,顾名思义,是树也是线段,但是这里的线段其实是用区间表示,而线段树也由于这个区间关系,可以用来维护区间的某些的性质,比如一个区间最值啊,和啊什么的。

建树和初始化

和大多树一样,在c语言中,树的建立可以用结构体数组来建,数组下标是指这个节点的位置,结构体里面包含了这个节点的左儿子和右儿子(树的特征),但是这个左右儿子是指这个节点包含的区间,

重点:只有左右儿子相等时(也就是这棵树的叶子)才会是实际的数据。

在结构体数组中还存放着另一个变量——用来记录区间的某些性质(另一个特征),比如sum,max,min什么的

代码:

struct tree{
    int l,r;
    long long sum,add;
}t[num];


void bulid(int p,int l,int r){
    t[p].l=l;t[p].r=r;
    if(l==r){
        t[p].sum=a[l];
        return;
    }
    int mid=l+r>>1;
    bulid(p*2,l,mid);
    bulid(p*2+1,mid+1,r);
    t[p].sum=t[p*2].sum+t[p*2+1].sum;
}

更新

  1. 懒标记
    这里要先介绍一个很神奇的地方,就是懒标记。在更新的时候,如果每次更新都把树从根到叶子都遍历一遍,虽然很好,但是会花时间,我们可以利用并查集优化的思想,就是搁置争议 ,简单来说,就是先把需要更新的数据先不动,等你要查询的时候,边查询边更新

  2. 更新(spread+change)

代码

void spread(int p)//p是节点的名字
{
    if(t[p].add){
        t[p*2].pre+=t[p].add*(t[p*2].r-t[p*2].l+1);
        t[p*2+1].pre+=t[p].add*(t[p*2+1].r-t[p*2+1].l+1);
        t[p*2].add+=t[p].add;
        t[p*2+1].add+=t[p].add;
        t[p].add=0;
    }
}

void change(int p,int x,int y,int z)//x,y是区间,z是要加或减的数
{
    if(x<=t[p].l && y>=t[p].r){
        t[p].pre+=(long long)z*(t[p].r-t[p].l+1);
        t[p].add+=z;
        return;
    }
    spread(p);
    int mid=t[p].l+t[p].r>>1;
    if(x<=mid) change(p*2,x,y,z);
    if(y>mid) change(p*2+1,x,y,z);
    t[p].pre=t[p*2].pre+t[p*2+1].pre;   
}

查询

上面讲了一部分,其实也就没剩下什么新东西了,就是dfs的基本套路。
代码


 long long ask(int p,int x,int y){
    if(x<=t[p].l && y>=t[p].r) return t[p].pre;
    spread(p);
    int mid=t[p].l+t[p].r>>1;
    long long ans=0;
    if(x<=mid) ans+=ask(p*2,x,y);
    if(y>mid) ans+=ask(p*2+1,x,y);
    return ans;
}

也就是有点二分的感觉

至于主函数部分就不贴了,就是按题写就好了。

谢谢大佬。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值