线段树

线段树主要用于多次查询和更改操作,时间复杂度是o(nlog(n)),n为区间长度

struct T
{
    ll val;
    ll addmark;
} tree[N<<4];
void init()
{
    memset(tree,0,sizeof(tree));
}
void build(int root,int l,int r)
{
    if(l==r)
        tree[root].val=W[l];
    else
    {
        int mid=(l+r)>>1;
        build(root<<1,l,mid);
        build(root<<1|1,mid+1,r);
        tree[root].val=tree[root<<1].val+tree[root<<1|1].val;
    }
}
void push_down(int root,int l,int r)
{
    if(tree[root].addmark!=0)
    {
        int mid=(l+r)>>1;
        tree[root<<1].addmark+=tree[root].addmark;
        tree[root<<1|1].addmark+=tree[root].addmark;
        tree[root<<1].val+=tree[root].addmark*(mid-l+1);
        tree[root<<1|1].val+=tree[root].addmark*(r-(mid+1)+1);
        /*
        求区间最值的更新方法为:
        tree[root<<1].mx+=tree[root].addmark;
        tree[root<<1|1].mx+=tree[root].addmark;
        */
        tree[root].addmark=0;
    }
}
ll query(int root,int l,int r,int b,int e)
{
    if(b>r||e<l)
        return 0;
    if(b<=l&&r<=e)
        return tree[root].val;
    push_down(root,l,r);//有延时标记时要加上这句
    int mid=(l+r)>>1;
    ll sum=0;
    sum+=query(root<<1,l,mid,b,e);
    sum+=query(root<<1|1,mid+1,r,b,e);
    return sum;
}
//单点更新。l,r:线段树的左右区间,ind:更新位置
void simple_updata(int root,int l,int r,int ind,ll add)
{
    if(l==r)
    {
        tree[root].val+=add;
        return ;
    }
    int mid=(l+r)>>1;
    if(ind<=mid)
        simple_updata(root<<1,l,mid,ind,add);
    else
        simple_updata(root<<1|1,mid+1,r,ind,add);
    tree[root].val=tree[root<<1].val+tree[root<<1|1].val;
}
//区间更新。root:根节点,l,r:线段是的左右区间,b,e:更新区间,add:更新的值
void interval_updata(int root,int l,int r,int b,int e,ll add)
{
    if(b>r||e<l)
        return ;
    if(b<=l&&r<=e)
    {
        tree[root].addmark+=add;
        tree[root].val+=add*(r-l+1);
        /*
        求区间最值的更新操作为:
        tree[root].mx+=add;
        */
        return ;
    }
    push_down(root,l,r);
    int mid=(l+r)>>1;
    interval_updata(root<<1,l,mid,b,e,add);
    interval_updata(root<<1|1,mid+1,r,b,e,add);
    tree[root].val=tree[root<<1].val+tree[root<<1|1].val;
}

//区间更新举例说明:当我们要对区间[0,2]的叶子节点增加2,利用区间查询的方法从根节点开始找到了非叶子节点[0-2],把它的值设置为1+2 = 3,并且把它的延迟标记设置为2,更新完毕;
//当我们要查询区间[0,1]内的最小值时,查找到区间[0,2]时,发现它的标记不为0,并且还要向下搜索,因此要把标记向下传递,把节点[0-1]的值设置为2+2 = 4,标记设置为2,节点[2-2]的值设置为1+2 = 3,
//标记设置为2(其实叶子节点的标志是不起作用的,这里是为了操作的一致性),然后返回查询结果:[0-1]节点的值4;当我们再次更新区间[0,1](增加3)时,
//查询到节点[0-1],发现它的标记值为2,因此把它的标记值设置为2+3 = 5,节点的值设置为4+3 = 7;
//其实当区间更新的区间左右值相等时([i,i]),就相当于单节点更新,单节点更新只是区间更新的特例。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值