zoukankan      html  css  js  c++  java
  • 线段树区间修改和查询和单点查询(线段树模板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了!!! 
    }
  • 相关阅读:
    bzoj1093[ZJOI2007]最大半连通子图(tarjan+拓扑排序+dp)
    tarjan强连通分量模板(pascal)
    二分图最小顶点覆盖数=最大匹配数的证明
    poj3041 Asteroids(二分图最小顶点覆盖、二分图匹配)
    bzoj4196[Noi2015]软件包管理器
    AEAI Portal 权限体系说明
    未来70%的人类将会失业
    工作中高效学习的方法
    如何正确的做事
    你真的会沟通吗? --下部
  • 原文地址:https://www.cnblogs.com/lipu123/p/13284514.html
Copyright © 2011-2022 走看看