【洛谷 P3372】【模板】线段树 1 pushdown 操作

10 篇文章 0 订阅
7 篇文章 0 订阅

题目描述
如题,已知一个数列,你需要进行下面两种操作:

将某区间每一个数加上 kk。
求出某区间每一个数的和。
输入格式
第一行包含两个整数 n, mn,m,分别表示该数列数字的个数和操作的总个数。

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

接下来 mm 行每行包含 33 或 44 个整数,表示一个操作,具体如下:

1 x y k:将区间 [x, y][x,y] 内每个数加上 kk。
2 x y:输出区间 [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%30% 的数据:n \le 8n≤8,m \le 10m≤10。
对于 70%70% 的数据:n \le {10}^3n≤10
3
,m \le {10}^4m≤10
4

对于 100%100% 的数据:1 \le n, m \le {10}^51≤n,m≤10
5

保证任意时刻数列中任意元素的和在 [-2^{63}, 2^{63})[−2
63
,2
63
) 内。

模板题,体会到了pushdown操作的绝妙魅力。这个题用到这个算法比普通线段树快了近4倍的速度。本题注意区间修改和区间查询都要先对当前结点进行pushdown(来自无数次WA的领悟)。

#include <iostream>
#include <cstdio>
#include <vector>
#include <cmath>
#include <cstring>
#include <string>
#include <map>
#include <algorithm>
#define maxn 1000000
using namespace std;
typedef long long ll;
ll n,m;
typedef struct Tree
{
    ll sum;
    ll l;
    ll r;
    ll lazy;
    Tree(){l=r=sum=lazy=0;}
} T;

T  a[maxn];
ll num[maxn];

void update(ll k)
{
    a[k].sum = a[k<<1].sum + a[k<<1|1].sum;
    return ;
}


void build_tree(ll k, ll l, ll r)
{
    a[k].l = l;
    a[k].r = r;
    a[k].lazy = 0;
    if(l==r)
    {
        a[k].sum = num[l];
        return ;
    }
    ll mid = (l+r)>>1;
    build_tree(k<<1,  l, mid);
    build_tree(k<<1|1 , mid+1, r);
    update(k);
}

void change(ll k,  ll pos,  ll obj)
{
    if(a[k].l==a[k].r)
    {
        a[k].sum = obj;
        return ;
    }
     ll mid = (a[k].l + a[k].r)>>1;
    if(pos<=mid)  change(k<<1, pos , obj);
    else change(k<<1|1,pos,obj);
    update(k);
}

void pushdown( ll k)
{
    if(a[k].l==a[k].r)
    {
        a[k].lazy = 0;
        return ;
    }
    a[k<<1].sum += (a[k<<1].r - a[k<<1].l + 1)*a[k].lazy;
    a[k<<1|1].sum += (a[k<<1|1].r - a[k<<1|1].l +1) *a[k].lazy;

    a[k<<1].lazy += a[k].lazy;
    a[ k<<1|1].lazy += a[k].lazy;
    a[k].lazy = 0;
}


void changeSegment( ll k,  ll l,  ll r, ll x)
{
    if(a[k].lazy) pushdown(k);
    if(a[k].l==l&&a[k].r==r)
    {
        a[k].sum += (a[k].r-a[k].l+1)*x;
        a[k].lazy += x;
        return ;
    }
     ll mid = (a[k].l+a[k].r)>>1;
    if(mid>=r)  changeSegment(k<<1,l,r,x);
    else if (mid<l) changeSegment(k<<1|1,l,r,x);
    else changeSegment(k<<1,l,mid,x) , changeSegment(k<<1|1,mid+1,r,x);
    update(k);
}


 ll query( ll k,  ll l,  ll r)
{
    if(a[k].lazy)  pushdown(k);
    if(a[k].l==l&&a[k].r==r)
        return a[k].sum;

     ll mid = (a[k].l+a[k].r)>>1;
    if(mid>=r) return query(k<<1,l,r);
    else if(mid<l) return query(k<<1|1,l,r);
    return query(k<<1,l,mid) + query(k<<1|1,mid+1,r);
}


int main()
{
    cin>>n>>m;
    for(int i=1; i<=n; i++)
        scanf("%lld",&num[i]);
    build_tree(1,1,n);
    while(m--)
    {
        ll cnt, x, y;
        scanf("%lld %lld %lld",&cnt,&x,&y);
        if(cnt==1)
        {
            ll val;
            scanf("%lld",&val);
            changeSegment(1,x,y,val);
        }
        else
        {
            printf("%lld\n", query(1,x,y));
        }
    }
    return 0;
}

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值