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, A1, A2, ... , 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 A1, A2, ... , AN. -1000000000 ≤ Ai ≤ 1000000000.
Each of the next Q lines represents an operation.
"C a b c" means adding c to each of Aa, Aa+1, ... , Ab. -10000 ≤ c ≤ 10000.
"Q a b" means querying the sum of Aa, Aa+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”,这个题耗了我一天加一上午,但也让我对线段树更加深入了解,试遍了各种错误,模板更加完善,,,也不错。。。。