线段树区间修改和查询和单点查询(线段树模板1)

Posted lipu123

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树区间修改和查询和单点查询(线段树模板1)相关的知识,希望对你有一定的参考价值。

https://www.luogu.com.cn/problem/P3372

题目描述

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

  1. 将某区间每一个数加上 kk。
  2. 求出某区间每一个数的和。

输入格式

第一行包含两个整数 n, mn,m,分别表示该数列数字的个数和操作的总个数。

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

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

  1. 1 x y k:将区间 [x, y][x,y] 内每个数加上 kk。
  2. 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 8n8,m le 10m10。
对于 70\%70% 的数据:n le {10}^3n103,m le {10}^4m104。
对于 100\%100% 的数据:1 le n, m le {10}^51n,m105。

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

【样例解释】

技术图片

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<map> 
#include <math.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+10;
int a[maxn]; 
inline int read()
{
    int x=0;char ch=getchar();
    while(ch<0||ch>9)ch=getchar();
    while(ch>=0&&ch<=9){x=x*10+ch-0;ch=getchar();}
    return x;
}
struct node{
    ll l;//l:左节点 r:右节点 
    ll r;//dat:当前节点的值 laze_tag:懒标记,记录改变的值,递归传值 
    ll date,laze;
}t[maxn];//四倍n 
void f(ll p,ll k){ 
    t[p].laze+=k;//懒标记传递
    t[p].date+=k*(t[p].r-t[p].l+1);//当前值加上所有节点总数*值     
}
void pushdown(ll p){//传懒标 
    f(p*2,t[p].laze);
    f(p*2+1,t[p].laze);
     //将懒标记的值传给下面的左右儿子节点
    t[p].laze=0;
    //复原懒标记 
}
void js(ll p,ll l,ll r){//建树 
    t[p].l=l;//记录左右节点 
    t[p].r=r;
    if(l==r){//到达底部返回值 
        t[p].date=a[l];
        return ;
    }
    ll mid=(l+r)/2;//中点
    js(p*2,l,mid);
    js(p*2+1,mid+1,r);
        //递归初始化
    t[p].date=t[p*2].date+t[p*2+1].date;
      //加上左右儿子节点 
}
void pushs(ll p,ll l,ll r,ll v){//区间加减
    if(t[p].l>=l&&t[p].r<=r){//如果区间被包含就修改并打上懒标记 
        t[p].date+=v*(t[p].r-t[p].l+1);//加上所有值
        t[p].laze+=v;//懒标记修改 
        return ;
    }
    pushdown(p);//查询懒标记,因为下面要递归 
    ll mid=(t[p].l+t[p].r)/2;//取中点
    if(l<=mid){
        pushs(p*2,l,r,v);//修改左边 
    }
    if(r>mid){
        pushs(p*2+1,l,r,v);//修改右边  
    }
    t[p].date=t[p*2].date+t[p*2+1].date;//回溯时加上左右儿子节点的值 
} 
ll outt(ll p,ll l){//单点查询 
    if(t[p].l==l&&t[p].r==l){//找到目标点就返回 
        return t[p].date;
    }
    pushdown(p);//先回复懒标记的值再传递,因为下面可能递归(要判断是否到了底部,就是这里出了问题QwQ)
    ll mid=(t[p].l+t[p].r)/2;//记录中点
    if(l<=mid) return outt(p*2,l);//找左边
    if(l>mid) return outt(p*2+1,l);//找右边 
}
ll check(ll p,ll l,ll r,ll x,ll y){
    if(l>=x&&r<=y){
        return t[p].date;
    }
    ll mid=(t[p].l+t[p].r)/2;
    ll ans=0;
    pushdown(p);
    if(x<=mid){
        ans+=check(p*2,l,mid,x,y); 
    }
    if(mid<y){
        ans+=check(p*2+1,mid+1,r,x,y);
    }
    return ans;
} 
int main(){
    int n,m; 
    n=read();m=read();//读入 
    for(int i=1;i<=n;i++)
        a[i]=read();
    js(1,1,n);//建树
    int z;
    int x,y,w; 
    for(int i=1;i<=m;i++){
        z=read();
        if(z==1){
            x=read(),y=read(),w=read();
            pushs(1,x,y,w);
        }
        else{
            x=read(),y=read();
            ll z=check(1,1,n,x,y);
            printf("%lld
",z);
        }
    }
    return 0;//华丽丽的结束,可以A掉树状数组2了!!! 
}

 

以上是关于线段树区间修改和查询和单点查询(线段树模板1)的主要内容,如果未能解决你的问题,请参考以下文章

线段树单点修改区间修改单点查询值区间查询最大值最小值区间和之模板

HDU 1166 - 敌兵布阵 - [单点修改区间查询zkw线段树]

线段树详解

线段树 建树 单点修改 点点/区间查询

数据结构——线段树

线段树 区间修改,单点查询