洛谷P3372 线段树1【模板】

题目

题目描述

如题,已知一个数列,你需要进行下面两种操作:
1.将某区间每一个数加上x
2.求出某区间每一个数的和

输入输出格式

输入格式:

第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。
第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。
接下来M行每行包含3或4个整数,表示一个操作,具体如下:
操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k
操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和

输出格式:

输出包含若干行整数,即为所有操作2的结果。

输入输出样例

输入

5 5
1 5 4 2 3
2 2 4
1 2 3 2
2 3 4
1 1 5 1
2 1 4

输出

11
8
20

说明

时空限制:1000ms,128M

数据规模:
对于30%的数据:N<=8,M<=10
对于70%的数据:N<=1000,M<=10000

对于100%的数据:N<=100000,M<=100000

分析

翻译过来就是:给你一颗线段树,区间修改或区间查询。
lazy标记法。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstring>
#include <vector>
#include <cstdlib>
#include <ctime>
//日常头文件
using namespace std;
//依然头文件
typedef long long ll;
//懒得打
const int N=100900;
//数据范围
ll ans;
//可能很大
ll a[N];
//可能很大
struct node
{
    ll sum;
    ll addv;
};

node v[N*4];

void Add(ll,ll,ll,ll);
//操作2第二步
void pushdown(ll,ll,ll);
//向下
void pushup(ll);
//向上
void buitre(ll,ll,ll);
//建树
ll Query(ll,ll,ll,ll,ll);
//区间查询
void update(ll,ll,ll,ll,ll,ll);
//区间修改
int main()
{
    int n;
    int m;

    scanf("%d",&n);
    scanf("%d",&m);

    for(int i=1;i<=n;i++)
    	scanf("%lld",&a[i]);
    //以上不解释
    buitre(1,n,1);
    //建树
    for(int i=1;i<=m;i++)
    {
        int opt;

        scanf("%d",&opt);

        if(opt==1)
        {
            int x;
            int y;
            int k;

            scanf("%d",&x);
            scanf("%d",&y);
            scanf("%d",&k);

            update(x,y,k,1,n,1);
        }
        else//我不信还有第三个操作嘎哈哈
        {
            int x;
            int y;

            scanf("%d",&x);
            scanf("%d",&y);

            ll answ;

            answ=Query(x,y,1,n,1);

            printf("%lld\n",answ);
        }
        //分开写是因为读入的数的个数不一样
        //也可以强制性写一起
    }
    //处理操作
    return 0;
}

void Add(ll l,ll r,ll C,ll rt)
{
    v[rt].sum+=(r-l+1)*C;

    v[rt].addv+=C;
}

void pushdown(ll l,ll r,ll rt)
{
    ll mid;

    mid=(l+r)/2;

    if(v[rt].addv)
    {
        ll C;

        C=v[rt].addv;

        Add(l,mid,C,rt<<1);
        Add(mid+1,r,C,rt<<1|1);

        v[rt].addv=0;
    }
}

void pushup(ll rt)
{
    v[rt].sum=v[rt*2].sum+v[rt*2+1].sum;
}

void buitre(ll l,ll r,ll rt)
{
    if(l==r)
    {
        v[rt].sum=a[l];

        return;
    }
    ll mid;
    mid=(l+r)/2;

    buitre(l,mid,2*rt);
    buitre(mid+1,r,2*rt+1);

    pushup(rt);
}

ll Query(ll L,ll R,ll l,ll r,ll rt)
{
    if(L<=l&&r<=R)
    	return v[rt].sum;
    ll mid;
    ll ans;

    mid=(l+r)>>1;
    ans=0;

    pushdown(l,r,rt);

    if(L<=mid)
    	ans+=Query(L,R,l,mid,rt<<1);

    if(R>mid)
    	ans+=Query(L,R,mid+1,r,rt<<1|1);

    return ans;
}

void update(ll L,ll R,ll C,ll l,ll r,ll rt)
{
    if(L<=l&&r<=R)
    {
        Add(l,r,C,rt);

        return;
    }

    ll mid;

    mid=(l+r)>>1;

    pushdown(l,r,rt);

    if(L<=mid)
    	update(L,R,C,l,mid,rt<<1);

    if(R>mid)
    	update(L,R,C,mid+1,r,rt<<1|1);

    pushup(rt);
}
//哦,好长
//妈呀终于完事了
调了半天我去
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值