线段树的lazy(poj3468)

A Simple Problem with Integers

Time Limit: 5000MS   Memory Limit: 131072K
Total Submissions: 73163   Accepted: 22585
Case Time Limit: 2000MS

Description

You have N integers, A1A2, ... , AN. You need to deal with two kinds of operations. One type of operation is to add some given number to each number in a given interval. The other is to ask for the sum of numbers in a given interval.

Input

The first line contains two numbers N and Q. 1 ≤ N,Q ≤ 100000.
The second line contains N numbers, the initial values of A1A2, ... , AN. -1000000000 ≤ Ai ≤ 1000000000.
Each of the next Q lines represents an operation.
"C a b c" means adding c to each of AaAa+1, ... , Ab. -10000 ≤ c ≤ 10000.
"Q a b" means querying the sum of AaAa+1, ... , Ab.

Output

You need to answer all Q commands in order. One answer in a line.

Sample Input

10 5
1 2 3 4 5 6 7 8 9 10
Q 4 4
Q 1 10
Q 2 4
C 3 6 3
Q 2 4

Sample Output

4
55
9
15

Hint

The sums may exceed the range of 32-bit integers.

Source

POJ Monthly--2007.11.25, Yang Yi

  最裸的线段树lazy标记

 1 #include <iostream>
 2 #include <cstdio>
 3 using namespace std;
 4 const int N = 100005;
 5 typedef  long long LL;
 6 LL sum[N<<2]; //sum用来存储每个节点的子节点数值的总和
 7 LL add[N<<2];//add用来记录该节点的每个数值应该加多少
 8 struct Node{
 9     int l,r;//表示改点的左右区间
10     int mid(){//结构体函数
11         return (l+r)>>1;
12     }
13 } tree[N<<2];
14
15 void PushUp(int rt){//算某一节点的左右孩子值的和
16     sum[rt] = sum[rt<<1] + sum[rt<<1|1];
17 }
18
19 void PushDown(int rt,int m){//rt当前节点  m此节点的区间长度
20     if(add[rt]!=0){//如果当前节点lazy标记不为 0
21         add[rt<<1] += add[rt];//左右孩子lazy累加
22         add[rt<<1|1] += add[rt];
23         sum[rt<<1] += add[rt]*(m-(m>>1));//更新计算左右孩子
24         sum[rt<<1|1] += add[rt] * (m>>1);
25         add[rt] = 0;//小细节,但很重要,不能忘记lazy用过后清零
26     }
27 }
28
29 void build(int l,int r,int rt){
30     tree[rt].l = l;
31     tree[rt].r = r;
32     add[rt] = 0;//lazy标记记为0
33     if(l == r){
34         scanf("%lld",&sum[rt]);//把子节点信息记录在sum里
35         return ;
36     }
37     int m = tree[rt].mid();
38     build(l,m,rt<<1);//建立左右孩子,这时编号是按照类似“宽搜”来编的
39     build(m+1,r,rt<<1|1);
40
41     PushUp(rt);//算出此节点的权值
42 }
43
44 void update(int c,int l,int r,int rt){//当前节点rt,在l和r区间上加上c
45     if(l<=tree[rt].l&&tree[rt].r<=r){//当前节点所表示的区间完全被所要更新的区间包含
46         add[rt]+=c;//lazy累加,add[i]数组是针对i左右孩子的
47         sum[rt]+=(LL)c*(tree[rt].r-tree[rt].l+1);//这句话很重要和下面的PushUP()类似
48         return;
49     }
50     PushDown(rt,tree[rt].r - tree[rt].l + 1);//用rt的lazy更新其子节点
51     int m = tree[rt].mid();
52     if(l<=m) update(c,l,r,rt<<1);
53     if(m+1<=r) update(c,l,r,rt<<1|1);
54     PushUp(rt);
55 }
56
57 LL query(int l,int r,int rt){//当前节点为rt,求l,到r的和
58     if(l<=tree[rt].l&&tree[rt].r<=r){//区间完全包含,可以直接返回
59         return sum[rt];
60     }
61     //因为此时用到rt节点,所以才更新lazy
62     PushDown(rt,tree[rt].r-tree[rt].l + 1);
63
64     int m = tree[rt].mid();
65     LL res = 0;
66
67     if(l<= m)//要求的区间有一部分在mid的左边
68         res += query(l,r,rt<<1);
69     if(m+1<=r)
70         res += query(l,r,rt<<1|1);
71     return res;
72 }
73
74 int main(){
75
76     int n,m;
77
78     scanf("%d %d",&n,&m);
79     build(1,n,1);
80
81     while(m--){
82     char ch[2];
83     scanf("%s",ch);
84     int a,b,c;
85     if(ch[0] == ‘Q‘){
86         scanf("%d %d", &a,&b);
87         printf("%lld\n",query(a,b,1));
88     }
89     else{
90         scanf("%d %d %d",&a,&b,&c);
91         cin>>a>>b>>c;
92         update(c,a,b,1);
93         }
94     }
95
96     return 0;
97 }

还有一种较好理解的方法:

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstdlib>
  4 #include<cmath>
  5 #include<cstring>
  6 #include<algorithm>
  7 using namespace std;
  8 typedef long long LL;
  9 const LL NN=100005;
 10 LL a[NN];
 11 LL N,tot,T,M;
 12 struct node{
 13     LL num;
 14     LL lc,rc;
 15     LL l,r;
 16     LL sum;
 17     LL lazy;
 18 };
 19
 20 node segtree[2000000];
 21
 22 void Build(LL root,LL l,LL r){
 23     segtree[root].l=l;
 24     segtree[root].r=r;
 25     segtree[root].lazy=0;
 26     segtree[root].num=tot;
 27     if(l==r){
 28         segtree[root].sum=a[l];
 29         return ;
 30     }
 31
 32     LL mid=(l+r)>>1;
 33
 34     segtree[root].lc=++tot;
 35     segtree[segtree[root].lc].num=tot;
 36     Build(tot,l,mid);
 37
 38     segtree[root].rc=++tot;
 39     segtree[segtree[root].rc].num=tot;
 40     Build(tot,mid+1,r);
 41     segtree[root].sum=segtree[segtree[root].lc].sum+
 42                 segtree[segtree[root].rc].sum;
 43
 44 }
 45
 46 void updatason(LL root){
 47     LL d=segtree[root].lazy;
 48     if(d!=0){
 49         segtree[segtree[root].lc].lazy+=(LL)d;
 50         segtree[segtree[root].rc].lazy+=(LL)d;
 51
 52         segtree[segtree[root].lc].sum+=(LL)d*(segtree[segtree[root].lc].r
 53                             -segtree[segtree[root].lc].l+1);
 54         segtree[segtree[root].rc].sum+=(LL)d*(segtree[segtree[root].rc].r
 55                             -segtree[segtree[root].rc].l+1);
 56         segtree[root].lazy=0;
 57     }
 58 }
 59
 60 LL query(LL root,LL l,LL r){
 61     if(l<=segtree[root].l&&segtree[root].r<=r){
 62         return segtree[root].sum;
 63     }
 64     updatason(root);
 65     LL ans=0;
 66     LL mid=(segtree[root].r+segtree[root].l)>>1;
 67     if(l<=mid) ans+=(LL)query(segtree[root].lc,l,r);
 68     if(mid+1<=r) ans+=(LL)query(segtree[root].rc,l,r);
 69     return ans;
 70 }
 71
 72 void changesect(LL root,LL l,LL r,LL d){
 73     if(l<=segtree[root].l&&segtree[root].r<=r){
 74         segtree[root].sum+=(LL)(segtree[root].r-segtree[root].l+1)*d;
 75         segtree[root].lazy+=(LL)d;
 76         return ;
 77     }
 78     updatason(root);
 79     LL mid=(segtree[root].r+segtree[root].l)>>1;
 80     if(l<=mid) changesect(segtree[root].lc,l,r,d);
 81     if(mid+1<=r) changesect(segtree[root].rc,l,r,d);
 82     segtree[root].sum=(LL)segtree[segtree[root].lc].sum+
 83                         (LL)segtree[segtree[root].rc].sum;
 84 }
 85 int main(){
 86
 87     scanf("%d%d",&N,&M);
 88     for(LL i=1;i<=N;i++)
 89         scanf("%lld",&a[i]);
 90     tot++;
 91     Build(1,1,N);
 92     LL ll,rr,de;
 93     char x[2];
 94     for(LL i=1;i<=M;i++){
 95         scanf("%s",x);
 96         if(x[0]==‘C‘){
 97             cin>>ll>>rr>>de;
 98             changesect(1,ll,rr,de);
 99             continue;
100         }
101         if(x[0]==‘Q‘){
102             cin>>ll>>rr;
103             printf("%lld\n",query(1,ll,rr));
104         }
105     }
106     return 0;
107 }

  由于此题数据较大,所以要将int 都改为long long 。但这也给了我一个惨痛的教训,如果要在定义变量的时候该类型,千万不要忘记改变函数返回值的类型和输入改成“lld”,这个题耗了我一天加一上午,但也让我对线段树更加深入了解,试遍了各种错误,模板更加完善,,,也不错。。。。

时间: 2024-10-11 07:20:50

线段树的lazy(poj3468)的相关文章

Mayor&#39;s posters(线段树+离散化+lazy)

Mayor's posters(线段树+离散化+lazy) 题目大意:在墙上贴海报,然后很多海报,一层又一层,问你最后可以看到多少张海报. 题目分析:数据范围很大,普通的线段树肯定超时+超内存,所以要用到离散化,离散化有基础的和稍微复杂一点的,然后这题要用到稍微复杂一点的,离散化简单的来说就是只取我们需要的值来用,比如说区间[1000,2000],[1990,2012] 我们用不到[-∞,999][1001,1989][1991,1999][2001,2011][2013,+∞]这些值,所以我只

POJ 3237 Tree (树链剖分 路径剖分 线段树的lazy标记)

题目链接:http://poj.org/problem?id=3237 一棵有边权的树,有3种操作. 树链剖分+线段树lazy标记.lazy为0表示没更新区间或者区间更新了2的倍数次,1表示为更新,每次更新异或1就可以. 熟悉线段树成段更新就很简单了,最初姿势不对一直wa,还是没有彻底理解lazy标记啊. 1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 using namespace st

算法模板——线段树之Lazy标记

一.前言 前面我们已经知道线段树能够进行单点修改和区间查询操作(基本线段树).那么如果需要修改的是一个区间该怎么办呢?如果是暴力修改到叶子节点,复杂度即为\(O(nlog_n)\),显然是十分不优秀的.那么我们能不能向区间查询一样把复杂度降到\(O(log_n)\)呢? 二.算法流程 线段树肯定是兹瓷\(O(log_n)\)修改的,否则发明它有何用处?所以,我我们现在需要知道,如何快速进行区间修改操作.首先,我们回顾下终止节点 假定我要在这个图上修改区间[2,8],我只要修改掉图上所有的终止节点

线段树初步&amp;&amp;lazy标记

线段树 一.概述: 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点. 对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b].因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度. 使用线段树可以快速的查找某一个节点在若干条线段中出现的次数,时间复杂度为O(logN).而未优化的空间复杂度为2N,因此有时需要离散化让空间压缩. 二.基本操作: 1

POJ 2777 count color(线段树,lazy标记)

这里有一个思想:我们在更新的时候不必要更新到叶子节点,只要更新到当前区间包含线段树区间即可. 设计一个标志位,更新到此. A Simple Problem with Integers 也是一个类似的题目 设计两个函数 push_down 将结点信息传递到下层节点(inc, sub,) push_up      将下层节点信息反馈到上层(max,min,count) #include <map> #include <set> #include <queue> #inclu

线段树(lazy)-hdu1689

题目描述: 现在Pudge想做一些操作.让我们将钩子的连续金属棒从1编号到N.对于每个操作,Pudge可以将连续金属棒(编号为X到Y)更改为铜棒.银棒或金棒.钩的总值是N根金属棒的总和.更确切地说,每种棍棒的价值计算如下:对于每个铜棒,其值为1.对于每个银棒,其值为2.对于每个金棒,其值为3.Pudge希望知道在执行操作后钩子的总值.你可以认为原来的钩子是用铜棒做的. 输入: 输入由几个测试用例组成.输入的第一行是实例的数目.不超过10例.对于每种情况,第一行包含一个整数N,1<=N<=100

POJ2777线段树染色+lazy

第一次写这样的题目,反正出现了各种坑爹问题,调了老半天,除了延迟标记外,这题还要开一个cnt数组用来存储各个区间内颜色的种类数, 每次改变颜色时,更新一次. #include <cstdio> #include <cstring> #include <cmath> #include <algorithm> #include <climits> #include <string> #include <iostream> #i

线段树复习

#1080 : 更为复杂的买卖房屋姿势 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi和小Ho都是游戏迷,"模拟都市"是他们非常喜欢的一个游戏,在这个游戏里面他们可以化身上帝模式,买卖房产. 在这个游戏里,会不断的发生如下两种事件:一种是房屋自发的涨价或者降价,而另一种是政府有关部门针对房价的硬性调控.房价的变化自然影响到小Hi和小Ho的决策,所以他们希望能够知道任意时刻某个街道中所有房屋的房价总和是多少--但是很不幸的,游戏本身并不提供这样的计算

线段树再练习

要不是为了写splay的区间旋转的下放,我才不会写线段树的lazy下放来练练手(我原来的lazy都是跟着函数走的..) 这个时候我们得对lazy进行重新的界定,那就是lazy对当前节点是不产生影响的,而是对它的儿子产生影响.也就是说,当我到了某一个[l,r]区间,我不仅要更新它的lazy值,还要更新本身的key值,然后在对父亲节点进行更新.具体而言,在查询和修改时,访问到某一个节点,我们就得将本身的lazy值下放,同时对儿子的key值进行更新以保证我原来的下放原则.同时根据下放的原则,那么我们就