P3372 【模板】线段树 1---洛谷

题目描述

题目描述
如题,已知一个数列,你需要进行下面两种操作:
1.将某区间每一个数加上 k。
2.求出某区间每一个数的和。
输入格式
第一行包含两个整数 n, m,分别表示该数列数字的个数和操作的总个数。

第二行包含 n个用空格分隔的整数,其中第 i 个数字表示数列第 i 项的初始值。

接下来 m 行每行包含 3 或 4 个整数,表示一个操作,具体如下:
1.1 x y k:将区间 [x, y]内每个数加上 k。
2.2 x y:输出区间 [x, y]内每个数的和。
输出格式
输出包含若干行整数,即为所有操作 2 的结果。

输入输出样例
输入 #1 复制
5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4
输出 #1 复制
11
8
20
说明/提示
对于 30% 的数据:n≤8,m≤10。
对于 70%的数据:n <=10^3,m≤10 ^4
对于 100% 的数据:1≤n,m≤10 ^5。

保证任意时刻数列中任意元素的和在 [-2^ 63, 2^63) 内。
【样例解释】
在这里插入图片描述

简单说下这道题目

这道题目就是输入线段树的模板就好了,要注意的地方是:
1.求区间和的函数,返回值必须是long long类型。
2.这题必须要用到pushdowm操作,pushdowm是线段树的魅力所在,如果没有用到pushdowm,它的操作数是暴力操作的4-5倍,所以如果是最初的线段树,那它还不如暴力,做这道题目绝对会超时。
通过这道题目,我发现了自己的错误,对于区间求和和区间修改,都需要pushdowm操作。

代码如下:

#include<algorithm>
#include<cstring>
#include<string>
#include<iostream>
using namespace std;
#define ll long long
typedef struct
{
    ll l,r,w,lazy=0;
} NODE;
NODE tree[400005];
int build(ll l,ll r,ll k)
{
    tree[k].l=l;
    tree[k].r=r;
    if(l==r)
    {
        cin>>tree[k].w;
        return 0;
    }
    else
    {
        ll mid;
        mid=l+(r-l)/2;
        build(l,mid,k<<1);
        build(mid+1,r,k<<1|1);
        tree[k].w=tree[k<<1].w+tree[k<<1|1].w;
    }
}
void pushdowm(int k)
{
    if(tree[k].l==tree[k].r)
    {
        tree[k].lazy=0;
        return ;
    }
    tree[k<<1].w+=(tree[k<<1].r-tree[k<<1].l+1)*tree[k].lazy;
    tree[k<<1|1].w+=(tree[k<<1|1].r-tree[k<<1|1].l+1)*tree[k].lazy;
    tree[k<<1].lazy+=tree[k].lazy;
    tree[k<<1|1].lazy+=tree[k].lazy;
    tree[k].lazy=0;
}
ll ask_interval(ll x,ll y,ll k)
{
    if(tree[k].lazy)
        pushdowm(k);
    if(tree[k].l==x&&tree[k].r==y)
        return tree[k].w;
    ll mid=tree[k].l+(tree[k].r-tree[k].l)/2;
    if(mid>=y)
        return ask_interval(x,y,k<<1);
    if(x>mid)
        return ask_interval(x,y,k<<1|1);
    return ask_interval(x,mid,k<<1)+ask_interval(mid+1,y,k<<1|1);
}
void add(int k,int x,int y,int t)
{
    if(tree[k].l==x&&tree[k].r==y)
    {
        tree[k].w+=(tree[k].r-tree[k].l+1)*t;
        tree[k].lazy+=t;
        return ;
    }
        if(tree[k].lazy)
        pushdowm(k);
    ll mid=tree[k].l+(tree[k].r-tree[k].l)/2;
    if(y<=mid)
        add(k<<1,x,y,t);
    else if(x>mid)
        add(k<<1|1,x,y,t);
    else
    {
        add(k<<1,x,mid,t);
        add(k<<1|1,mid+1,y,t);
    }
    tree[k].w=tree[k<<1].w+tree[k<<1|1].w;
}
int main()
{
    int n,m;
    cin>>n>>m;
    build(1,n,1);
    for(int i=1; i<=m; i++)
    {
        int flag,a,b,k;
        cin>>flag>>a>>b;
        if(flag==2)
        {
            ll ans=0;
            ans=ask_interval(a,b,1);
            cout<<ans<<endl;
        }
        else if(flag==1)
        {
            cin>>k;
            add(1,a,b,k);
        }
    }
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_春与修罗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值