【线段树】【P3372】模板-线段树

百度百科

Definition&Solution

  线段树是一种log级别的树形结构,可以处理区间修改以及区间查询问题。期望情况下,复杂度为O(nlogn)。

   核心思想见百度百科,线段树即将每个线段分成左右两个线段做左右子树。一个线段没有子树,当且仅当线段表示的区间为[a,a]。

   由于编号为k的节点的子节点为2k以及2k+1,线段树可以快速的递归左右叶节点。

   lazy标记:当进行区间修改的时候,如果一个区间整体全部被包含于要修改的区间,则可以将该区间的值修改后,将lazy标记打在区间上,不再递归左右区间。

   例如,要修改[15,30]区间整体+2,当前区间为[16,24],被包含于要修改的区间。记代表区间[16,24]的节点编号为k,则tree[k]+=2*(24-16+1),同时lazy[k]+=2。

   在下次修改或查询到k节点时,进行lazy的下放,即如下代码

inline void Free(cl l,cl r,cl p) {
    ll m=(l+r)>>1,dp=p<<1;
    tree[dp]+=(m-l+1)*lazy[p];tree[dp+1]+=(r-m)*lazy[p];
    lazy[dp]+=lazy[p];lazy[dp+1]+=lazy[p];
    lazy[p]=0;
}

   注意:被打上lazy标记的区间实际上已经修改完区间和,每次free修改的是子区间

Example

传送门

Description

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

1.将某区间每一个数加上x

2.求出某区间每一个数的和

Input

第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。

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

接下来M行每行包含3或4个整数,表示一个操作,具体如下:

操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k

操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和

Output

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

Sample Input

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

Sample Output

11
8
20

Hint

时空限制:1000ms,128M

数据规模:

对于30%的数据:N<=8,M<=10

对于70%的数据:N<=1000,M<=10000

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

Solution

模板题。有一些需要注意的地方会在summary写明

Code

#include<cstdio>
#define maxn 100010
#define maxt 400010
#define ll long long int
#define cl const long long int

inline void qr(long long &x) {
    char ch=getchar();long long f=1;
    while(ch>‘9‘||ch<‘0‘)    {
        if(ch==‘-‘)    f=-1;
        ch=getchar();
    }
    while(ch>=‘0‘&&ch<=‘9‘)    x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    x*=f;
    return;
}

inline long long max(const long long &a,const long long &b) {if(a>b) return a;else return b;}
inline long long min(const long long &a,const long long &b) {if(a<b) return a;else return b;}
inline long long abs(const long long &x) {if(x>0) return x;else return -x;}

inline void swap(long long &a,long long &b) {
    long long c=a;a=b;b=c;return;
}

ll n,m,MU[maxn],sign,a,b,c;

ll tree[maxt],lazy[maxt];

void build(const ll l,const ll r,const ll p) {
    if(l>r)    return;
    if(l==r) {tree[p]=MU[l];return;}
    ll m=(l+r)>>1,dp=p<<1;
    build(l,m,dp);build(m+1,r,dp+1);
    tree[p]=tree[dp]+tree[dp+1];
}

inline void Free(cl l,cl r,cl p) {
    ll m=(l+r)>>1,dp=p<<1;
    tree[dp]+=(m-l+1)*lazy[p];tree[dp+1]+=(r-m)*lazy[p];
    lazy[dp]+=lazy[p];lazy[dp+1]+=lazy[p];
    lazy[p]=0;
}

inline void wohenlan(cl l,cl r,cl p,cl v) {tree[p]+=(r-l+1)*v;lazy[p]+=v;}

void add(cl l,cl r,cl p,cl aiml,cl aimr,cl v) {
    if(l>r) return;
    if(l>aimr||r<aiml) {return;}
    if(l>=aiml&&r<=aimr) {wohenlan(l,r,p,v);return;}
    Free(l,r,p);
    ll m=(l+r)>>1,dp=p<<1;
    add(l,m,dp,aiml,aimr,v);add(m+1,r,dp+1,aiml,aimr,v);
    tree[p]=tree[dp]+tree[dp+1];
}

ll ask(cl l,cl r,cl p,cl aiml,cl aimr) {
    if(l>r) return 0;
    if(l>aimr||r<aiml) {return 0;}
    if(l>=aiml&&r<=aimr) {return tree[p];}
    Free(l,r,p);
    ll m=(l+r)>>1,dp=p<<1;
    return ask(l,m,dp,aiml,aimr)+ask(m+1,r,dp+1,aiml,aimr);
}

int main() {
    qr(n);qr(m);
    for(int i=1;i<=n;++i) qr(MU[i]);
    build(1ll,n,1ll);
    while(m--) {
        sign=a=b=0;qr(sign);qr(a);qr(b);
        if(sign==1) {
            c=0;qr(c);
            add(1,n,1,a,b,c);
        }
        else printf("%lld\n",ask(1, n, 1, a, b));
    }
    return 0;
}

Summary

  1、线段树大小要开4*n。理论上线段树会有2*n个子节点,但是试试这棵线段树:1 2 3 4 5

      如图所示:

         可以看到,节点数确实是6*2-1=11个,但是由于我们每个节点编号都严格按照母节点*2(+1)进行编号,所以我们的编号开到了2*n之外。开4*n是比较保险的。

    2、注意对lazy标记的free操作要在确定区间可以再分以后进行。即先写

if(l>=aiml&&r<=aimr) {wohenlan(l,r,p,v);return;}

      或

if(l>=aiml&&r<=aimr) {return tree[p];}

      后,如果没有return,则证明区间一定是可再分的,即还没有递归到叶节点,这时才可以进行free操作。否则的话考虑在叶节点的编号可能大于2*n,我们在叶节点free了一下,标记被下放到了4*n以外……

      然后你就炸了。

原文地址:https://www.cnblogs.com/yifusuyi/p/9255229.html

时间: 2024-11-05 23:37:59

【线段树】【P3372】模板-线段树的相关文章

可持久化线段树 1(主席树模板)

[模板]可持久化线段树 1(主席树)(luogu) (本人的模板) Description 题目背景 这是个非常经典的主席树入门题——静态区间第 kk 小数据已经过加强,请使用主席树.同时请注意常数优化 题目描述 如题,给定 nn 个整数构成的序列,将对于指定的闭区间查询其区间内的第 kk 小值. 输入格式 第一行包含两个正整数 n,mn,m,分别表示序列的长度和查询的个数.第二行包含 nn 个整数,表示这个序列各项的数字.接下来 mm 行每行包含三个整数 l, r, kl,r,k , 表示查询

线段树---分析 &amp;&amp; 模板总结

线段树:(转) 数据结构专题---线段树:http://blog.csdn.net/metalseed/article/details/8039326 线段树总结:http://blog.csdn.net/shiqi_614/article/details/8228102 概述: 线段树,类似区间树,是一个完全二叉树,它在各个节点保存一条线段(数组中的一段子数组),主要用于高效解决连续区间的动态查询问题,由于二叉结构的特性,它基本能保持每个操作的复杂度为O(lgN)! 性质:父亲的区间是[a,b

Pascal 线段树 lazy-tag 模板

先说下我的代码风格(很丑,勿喷) maxn表示最大空间的四倍 tree数组表示求和的线段树 delta表示增减的增量标记 sign表示覆盖的标记 delta,sign实际上都是lazy标志 pushdown表示标记下传 pushup表示标记上传(即求和,区间最值) update表示数据更新 线段树(segment tree)是一种特别有用的数据结构,我们在维护区间各种信息的时候它就是利器.可能读者嫌线段树代码太长,不想写,而树状数组代码简洁易于便携,但是我在这里想说,线段树能做到的很多东西树状数

算法模板——线段树

前言 线段树作为高级数据结构,可以做非常非常多的事情,那么线段树到底是什么呢,我们就此了解下 一.基本概念 线段树并非什么特别高级的东西,顾名思义,它也就是一棵树.那么为什么叫线段树呢?因为树的节点上存的就是一些区间,也就是线段.那么它长啥样呢? 嗯,如上图,就是一个区间[1,9]的线段树.有些节点是叶子节点,叶子节点长度为1,不能继续往下分.叶子节点记录的信息是最基本的信息,而其他非叶子节点记录的就是两个儿子信息的合并(合并的方法有很多,具体情况具体分析).线段树的左右区间分别为\([l,mi

HDU 1542.Atlantis-线段树求矩形面积并(离散化、扫描线/线段树)-贴模板

好久没写过博客了,这学期不是很有热情去写博客,写过的题也懒得写题解.现在来水一水博客,写一下若干年前的题目的题解. Atlantis Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 21978    Accepted Submission(s): 8714 Problem Description There are several anc

[模板] 线段树合并

线段树合并 把若干棵叶子节点总数为 \(n\) 的线段树通过某种顺序合并成一棵线段树. 时间复杂度 \(O(n \log n)\). 时间复杂度分析 考虑两颗线段树合并, 复杂度为这两颗线段树的相同节点个数. 这可以看作是删除的节点个数. 那么所有线段树合并, 所有节点最多被删除一次. 时间复杂度即为 \(O(n \log n)\). 线段树合并的空间复杂度也为 \(O(n \log n)\), 而可持久化的线段树合并 (见下) 使用的空间不超过 \(2 \cdot n\log n\). 普通的

[bzoj3932][CQOI2015]任务查询系统-题解[主席树][权值线段树]

Description 最近实验室正在为其管理的超级计算机编制一套任务管理系统,而你被安排完成其中的查询部分.超级计算机中的 任务用三元组(Si,Ei,Pi)描述,(Si,Ei,Pi)表示任务从第Si秒开始,在第Ei秒后结束(第Si秒和Ei秒任务也在运行 ),其优先级为Pi.同一时间可能有多个任务同时执行,它们的优先级可能相同,也可能不同.调度系统会经常向 查询系统询问,第Xi秒正在运行的任务中,优先级最小的Ki个任务(即将任务按照优先级从小到大排序后取前Ki个 )的优先级之和是多少.特别的,如

HDU4897 (树链剖分+线段树)

Problem Little Devil I (HDU4897) 题目大意 给定一棵树,每条边的颜色为黑或白,起始时均为白. 支持3种操作: 操作1:将a->b的路径中的所有边的颜色翻转. 操作2:将所有 有且仅有一个点在a->b的路径中 的边的颜色翻转. 操作3:询问a->b的路径中的黑色边数量. 解题分析 考虑操作1,只需正常的树链剖分+线段树维护即可.用线段树维护每条边,tag_1[i]表示该区间中的黑色边数量. 考虑操作2,一个节点相邻的边只可能为重链和轻链,且重链的数目小于等于

【BZOJ2243】[SDOI2011]染色 树链剖分+线段树

[BZOJ2243][SDOI2011]染色 Description 给定一棵有n个节点的无根树和m个操作,操作有2类: 1.将节点a到节点b路径上所有点都染成颜色c: 2.询问节点a到节点b路径上的颜色段数量(连续相同颜色被认为是同一段),如"112221"由3段组成:"11"."222"和"1". 请你写一个程序依次完成这m个操作. Input 第一行包含2个整数n和m,分别表示节点数和操作数: 第二行包含n个正整数表示n