线段树再练习

要不是为了写splay的区间旋转的下放,我才不会写线段树的lazy下放来练练手(我原来的lazy都是跟着函数走的。。)

这个时候我们得对lazy进行重新的界定,那就是lazy对当前节点是不产生影响的,而是对它的儿子产生影响。也就是说,当我到了某一个[l,r]区间,我不仅要更新它的lazy值,还要更新本身的key值,然后在对父亲节点进行更新。具体而言,在查询和修改时,访问到某一个节点,我们就得将本身的lazy值下放,同时对儿子的key值进行更新以保证我原来的下放原则。同时根据下放的原则,那么我们就会发现,这个节点的值的更新在标记下放后就不会再次在同一次操作过程中被更新,那么就直接对于这个节点的key进行更新即可。

说那么多,还是代码比较重要!

+ ?





1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

#include <iostream>

#include <cstdio>

#include <cstring>

#include <algorithm>

using
namespace std;

typedef
long long
ll;

const
ll maxn = 200010;

struct
node{

    ll key,lazy;

    node *l,*r;

    node(){

        key = 0;lazy = 0; l = NULL; r = NULL;

    }

}e[maxn*3];ll ne = 0;

node* build(ll l,ll r){

    node* now = e + ne ++;

    if(l ^ r){

        ll mid = (l+r)>>1;

        now->l = build(l,mid);

        now->r = build(mid+1,r);

    }

    return
now;

}

void
pd(node* now,ll l,ll r){

    if(now->l != 0){

        ll mid = (l+r)>>1;

        now->l->lazy += now->lazy; now->l->key += (mid-l+1)*now->lazy;

        now->r->lazy += now->lazy; now->r->key += (r-mid)*now->lazy;

        now->lazy = 0;

    }

}

void
add(node* now,ll l,ll r,ll ls,ll rs,ll v){

    if(ls == l && r == rs){

        now->lazy += v;

        now->key += (r-l+1)*v;

    }

    else{

        ll mid = (l+r)>>1;

        pd(now,l,r);

        if(ls >= mid+1) add(now->r,mid+1,r,ls,rs,v);

        else
if(rs <= mid) add(now->l,l,mid,ls,rs,v);

        else
add(now->l,l,mid,ls,mid,v),add(now->r,mid+1,r,mid+1,rs,v);

        now->key = now->l->key + now->r->key;

    }

}

ll ask(node* now,ll l,ll r,ll ls,ll rs){

    if(ls == l && rs == r) return
now->key;

    else{

        pd(now,l,r);

        ll mid = (l+r)>>1;

        if(ls >= mid+1) return
ask(now->r,mid+1,r,ls,rs);

        else
if(rs <= mid) return
ask(now->l,l,mid,ls,rs);

        else
return ask(now->l,l,mid,ls,mid)+ask(now->r,mid+1,r,mid+1,rs);

    }

}

node* root;

ll n,m;

ll a[maxn];

void
test(node* now,ll l,ll r){

    cout <<l <<" "<<r<<" "<<now->key<<" "<<now->lazy<<endl;

    if(l^r){

        ll mid = (l+r)>>1;

        test(now->l,l,mid);

        test(now->r,mid+1,r);

    }

}

int
main(){

    freopen("cs.in","r",stdin);

    scanf("%lld",&n);

    root = build(1,n);

    for(ll i = 1; i <= n; i++){

        scanf("%lld",&a[i]);

        add(root,1,n,i,i,a[i]);

    }

    scanf("%lld",&m);

    while(m--){

        ll temp = 0;

        scanf("%lld",&temp);

        if(temp == 1){

            ll ls,rs,v;

            scanf("%lld%lld%lld",&ls,&rs,&v);

            add(root,1,n,ls,rs,v);

        }

        else
if(temp == 2){

            ll ls,rs;

            scanf("%lld%lld",&ls,&rs);

            printf("%lld\n",ask(root,1,n,ls,rs));

        }

    }

    return
0;

}

线段树再练习,码迷,mamicode.com

时间: 2024-12-10 03:05:26

线段树再练习的相关文章

hdu 1394 Minimum Inversion Number(这道题改日我要用线段树再做一次哟~)

Problem Description The inversion number of a given number sequence a1, a2, ..., an is the number of pairs (ai, aj) that satisfy i < j and ai > aj. For a given sequence of numbers a1, a2, ..., an, if we move the first m >= 0 numbers to the end of

SYSU 1686 线段树 lazy

区间更新+lazy I 操作 l-r 区间+c C操作 l-r区间的最大值,并把最大值删除 线段树再加个记录最大值的位置即可 #include "stdio.h" #include "string.h" int ans,mark; struct node { int l,r,Max,mark,lazy; }data[400010]; void Pushdown(int k) { if (data[k].l==data[k].r) return ; if (data[

【CF875E】Delivery Club 二分+线段树

[CF875E]Delivery Club 题意:有n个快递需要依次接收,这n个快递分部在x轴上,第i个快递的位置是xi.有两个快递员,一开始分别在s0,s1,你可以任意安排哪个人收哪个快递,前提是一个快递员收快递是另一个快递员不能移动(也就是说他只有在收快递时能移动),并且要保证任何时候两人的距离不超过k.问你k最小是多少. n<=10^5,xi<=10^9 题解:二分是显然的.我们可以用f[i][a][b]表示收第i个快递时,两个快递员一个在a,一个在b是否可行,又因为a或b一定等于i,所

权值线段树&amp;&amp;可持久化线段树&amp;&amp;主席树

权值线段树 顾名思义,就是以权值为下标建立的线段树. 现在让我们来考虑考虑上面那句话的产生的三个小问题: 1. 如果说权值作为下标了,那这颗线段树里存什么呢? ----- 这颗线段树中, 记录每个值出现的次数 2.权值很大怎么办?数组空间不够啊 ----- 可以先离散化,再记录 3.那权值线段树到底是用来干嘛的呢? ----- 可以快速求出第k小值(其实主要还是为了主席树做铺垫啦) 那第k小值该怎么求呢??? 从树根依次往下 若当前值K大于左儿子的值,则将K-=左儿子的值,然后访问右儿子 若当前

hihoCoder #1077 RMQ问题再临-线段树

#1077 : RMQ问题再临-线段树 Time Limit:10000ms Case Time Limit:1000ms Memory Limit:256MB 描述 上回说到:小Hi给小Ho出了这样一道问题:假设整个货架上从左到右摆放了N种商品,并且依次标号为1到N,每次小Hi都给出一段区间[L, R],小Ho要做的是选出标号在这个区间内的所有商品重量最轻的一种,并且告诉小Hi这个商品的重量.但是在这个过程中,可能会因为其他人的各种行为,对某些位置上的商品的重量产生改变(如更换了其他种类的商品

POJ 2155 二维线段树 经典的记录所有修改再统一遍历 单点查询

本来是想找一个二维线段树涉及懒惰标记的,一看这个题,区间修改,单点查询,以为是懒惰标记,敲到一半发现这二维线段树就不适合懒惰标记,你更新了某段的某列,但其实其他段的相应列也要打标记,但因为区间不一样,又不好打...也可能是我这是在套用一维线段树的思想,还有更好的二维线段树懒惰标记方法 反正到现在我还没搞定二维线段树的懒惰标记,因为这道题不用懒惰标记,因为是二进制序列,区间修改仅限于翻转操作,那就只要记录每次操作,最后查询的时候从上往下把所有修改都来上一遍,就可以了.就类似于树状数组的第二种用法,

Hihocoder #1077 : RMQ问题再临-线段树(线段树:结构体建树+更新叶子往上+查询+巧妙使用father[]+线段树数组要开大4倍 *【模板】)

#1077 : RMQ问题再临-线段树 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 上回说到:小Hi给小Ho出了这样一道问题:假设整个货架上从左到右摆放了N种商品,并且依次标号为1到N,每次小Hi都给出一段区间[L, R],小Ho要做的是选出标号在这个区间内的所有商品重量最轻的一种,并且告诉小Hi这个商品的重量.但是在这个过程中,可能会因为其他人的各种行为,对 某些位置上的商品的重量产生改变(如更换了其他种类的商品). 小Ho提出了两种非常简单的方法,但是都不能

线段树 区间更新(更新区间[x,y]的值,再求任意区间[x,y]的和)

#1078 : 线段树的区间修改 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 对于小Ho表现出的对线段树的理解,小Hi表示挺满意的,但是满意就够了么?于是小Hi将问题改了改,又出给了小Ho: 假设货架上从左到右摆放了N种商品,并且依次标号为1到N,其中标号为i的商品的价格为Pi.小Hi的每次操作分为两种可能,第一种是修改价格--小Hi给出一段区间[L, R]和一个新的价格NewP,所有标号在这段区间中的商品的价格都变成NewP.第二种操作是询问--小Hi给出一段

bzoj1798: [Ahoi2009]Seq 维护序列seq 线段树

题目传送门 这道题就是线段树 先传乘法标记再传加法 #include<cstdio> #include<cstring> #include<algorithm> #define LL long long using namespace std; const int M=400010; LL read(){ LL ans=0,f=1,c=getchar(); while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();}