线段树 - A Simple Problem with Integers - POJ - 3468

线段树 - A Simple Problem with Integers - POJ - 3468

题意:

给 定 长 度 为 N 的 序 列 A 1 , A 2 , . . . , A N , 共 有 两 种 操 作 : 给定长度为N的序列A_1,A_2,...,A_N,共有两种操作: NA1,A2,...,AN

1 、 “ C   l   r   d ” , 表 示 把 A [ l ] , A [ l + 1 ] , … , A [ r ] 都 加 上 d 。 1、“C \ l\ r\ d”,表示把 A[l],A[l+1],…,A[r] 都加上 d。 1C l r dA[l],A[l+1],,A[r]d

2 、 “ Q   l   r ” , 表 示 询 问 数 列 中 区 间 [ l , r ] 的 数 的 和 。 2、“Q \ l\ r”,表示询问 数列中区间 [l,r] 的数的和。 2Q l r[l,r]

共 Q 组 询 问 。 共Q组询问。 Q

Sample Input

10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

Sample Output

4
55
9
15

数据范围:

1 ≤ N , Q ≤ 100000 , − 1000000000 ≤ A i ≤ 1000000000 , − 10000 ≤ d ≤ 10000. 1 ≤ N,Q ≤ 100000,-1000000000 ≤ A_i ≤ 1000000000,-10000 ≤ d ≤ 10000. 1N,Q1000001000000000Ai100000000010000d10000.
T i m e   l i m i t : 5000 m s , C a s e   t i m e   l i m i t : 2000 m s , M e m o r y   l i m i t : 131072 k B Time \ limit:5000 ms,Case \ time \ limit:2000 ms,Memory \ limit:131072 kB Time limit:5000ms,Case time limit:2000ms,Memory limit:131072kB


分析:

两 种 操 作 分 别 是 区 间 修 改 和 区 间 求 和 , 可 以 通 过 差 分 数 组 , 转 化 为 单 点 修 改 , 再 通 过 树 状 数 组 求 前 缀 和 。 两种操作分别是区间修改和区间求和,可以通过差分数组,转化为单点修改,再通过树状数组求前缀和。
详 见 — — 详见—— 树状数组 - 一个简单的整数问题》。

这 里 用 线 段 树 来 解 决 这 个 问 题 。 这里用线段树来解决这个问题。 线

做 区 间 加 法 , 需 要 在 节 点 结 构 体 中 添 加 懒 标 记 a d d 。 做区间加法,需要在节点结构体中添加懒标记add。 add

表 示 某 段 区 间 内 的 所 有 数 都 增 加 a d d 。 表示某段区间内的所有数都增加add。 add

需 要 注 意 的 是 , 懒 标 记 只 有 在 查 询 或 者 修 改 子 区 间 的 时 候 , 才 会 向 下 传 递 。 每 次 传 递 后 , 要 将 父 节 点 的 懒 标 记 清 空 。 需要注意的是,懒标记只有在查询或者修改子区间的时候,才会向下传递。每次传递后,要将父节点的懒标记清空。

因 为 我 们 要 保 证 每 个 节 点 表 示 的 区 间 内 的 懒 标 记 要 相 同 , 不 能 整 个 区 间 的 几 段 a d d 的 值 不 同 。 因为我们要保证每个节点表示的区间内的懒标记要相同,不能整个区间的几段add的值不同。 add

对 懒 标 记 有 个 p u s h d o w n 操 作 , 记 父 节 点 为 r o o t , 向 下 传 递 懒 标 记 a d d : 对懒标记有个pushdown操作,记父节点为root,向下传递懒标记add: pushdownrootadd:

左 区 间 l e f t : l e f t . a d d + = r o o t . a d d , l e f t . s u m + = c n t l e f t × l e f t . a d d , 其 中 c n t l e f t 为 左 区 间 中 数 的 个 数 。 左区间left:left.add+=root.add,left.sum+=cnt_{left}×left.add,其中cnt_{left}为左区间中数的个数。 left:left.add+=root.addleft.sum+=cntleft×left.addcntleft

右 区 间 同 理 。 最 后 不 要 忘 记 清 空 r o o t . a d d = 0 。 右区间同理。最后不要忘记清空root.add=0。 root.add=0

代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>

#define ll long long

using namespace std;

const int N=1e5+10;

struct node
{
    int l,r;
    ll sum,add;
}tr[N*4];

int n,m;
int w[N];

void pushup(int u)
{
    tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
}

void pushdown(int u)
{
    node &root=tr[u], &left=tr[u<<1], &right=tr[u<<1|1];
    if(root.add)
    {
        left.add+=root.add, left.sum+=(ll)(left.r-left.l+1)*root.add;
        right.add+=root.add, right.sum+=(ll)(right.r-right.l+1)*root.add;
        root.add=0;
    }
}

void build(int u,int l,int r)
{
    if(l==r) tr[u]={l,r,w[r],0};
    else
    {
        tr[u]={l,r};
        int mid=l+r>>1;
        build(u<<1,l,mid),build(u<<1|1,mid+1,r);
        pushup(u);
    }
}

void modify(int u,int l,int r,int d)
{
    if(l<=tr[u].l&&tr[u].r<=r)
    {
        tr[u].sum+=(ll)(tr[u].r-tr[u].l+1)*d;
        tr[u].add+=d;
    }
    else  //区间分裂开了,有几段的懒标记不同
    {
        pushdown(u);
        int mid=tr[u].l+tr[u].r>>1;
        if(l<=mid) modify(u<<1,l,r,d);
        if(r>mid) modify(u<<1|1,l,r,d);
        pushup(u);
    }
}

ll query(int u,int l,int r)
{
    if(l<=tr[u].l&&tr[u].r<=r) return tr[u].sum;

    pushdown(u);
    int mid=tr[u].l+tr[u].r>>1;
    ll sum=0;
    if(l<=mid) sum+=query(u<<1,l,r);
    if(r>mid) sum+=query(u<<1|1,l,r);
    return sum;
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++) scanf("%d",&w[i]);
    build(1,1,n);
    
    char op[2];
    int l,r,d;
    while(m--)
    {
        scanf("%s%d%d",op,&l,&r);
        if(*op=='Q') printf("%lld\n",query(1,l,r));
        else
        {
            scanf("%d",&d);
            modify(1,l,r,d);
        }
    }
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值