NYOJ士兵杀敌5(线段树的区间更新)

初学线段树--区域更新
实现原理:初始的时候,给每个树节点都给一个标记,当你在更新某一段区间的时候,要映入一个标记数组,来标记该树节点更新后是否要将更新其子节点,如果该节点的标记值不为空,则表示要对该节点的子节点进行更新,更新完后取消该节点的标记;反之,则无需更新该节点的子节点,该线段树所对应区间的值可以直接拿来使用。
下面上代码(题目来自nyoj士兵杀敌5):

#include<iostream>
#include<cstdio>
using namespace std;
typedef struct Node
{
    int value;
    int l;
    int r;
};
Node tree[1000050<<2];
int col[1000050<<2];
int n,c,q;
int mi,ni,add,m,n1;
void build_tree(int v,int l,int r)
{
    tree[v].l=l;
    tree[v].r=r;
    if(l==r)
    {
        col[v]=0;
        tree[v].value=0;
        return;
    }

    int mid=(l+r)/2;
    build_tree(v<<1,l,mid);
    build_tree(v<<1|1,mid+1,r);
    tree[v].value=tree[v<<1].value+tree[v<<1|1].value;
}
void pushDown(int v)
{
    if(col[v]!=0)
    {
        col[v<<1]=col[v<<1|1]=col[v];//标记传给子节点
        tree[v<<1].value=(tree[v<<1].r-tree[v<<1].l+1)*col[v];
        tree[v<<1|1].value=(tree[v<<1|1].r-tree[v<<1|1].l+1)*col[v];
        col[v]=0;//本身撤销标记
    }
}
void update(int v,int vl,int vr,int l,int r,int add)
{
    if(vr<l||vl>r)
        return;
    if(l<=vl&&vr<=r)
    {
        col[v]=add;
        tree[v].value+=(vr-vl+1)*add;
        return;
    }

    pushDown(v);//进行更新,将原来的标记撤销,标记到子节点

    int mid=(vl+vr)/2;
    update(v<<1,vl,mid,l,r,add);
    update(v<<1|1,mid+1,vr,l,r,add);
    tree[v].value=tree[v<<1].value+tree[v<<1|1].value;

}
int query(int v,int vl,int vr,int l,int r)//这里注意,查询的时候也必须要进行区域更新,update结束后,
{//刚好是子节点的一个区间,这样结束的话标记还残留,因此如果查询的区间不是正好符合该子节点的区间的话
    //就必须要进行子节点的更新,不然会出错
    if(vl>r||vr<l)
        return 0;
    if(l<=vl&&vr<=r)
    {
        return tree[v].value;
    }

    pushDown(v);

    int mid=(vl+vr)/2;
    return  query(v<<1,vl,mid,l,r)+query(v<<1|1,mid+1,vr,l,r);

}
int main()
{
    scanf("%d%d%d",&n,&c,&q);
    build_tree(1,1,n);

    for(int i=0;i<c;i++)
    {
        scanf("%d%d%d",&mi,&ni,&add);
        update(1,1,n,mi,ni,add);
    }
    for(int i=0;i<q;i++)
    {
        scanf("%d%d",&m,&n1);
        int ans=query(1,1,n,m,n1);
        printf("%d\n",ans);
    }
return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值