Pku3468 A Simple Problem with Integers
Posted cutemush
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Pku3468 A Simple Problem with Integers相关的知识,希望对你有一定的参考价值。
You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of op
eration 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.
1.给[a ,b]整体上加上一个常数c。
2.查询[a ,b]区间的和。
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. The sums may exceed the range of 32-bit integers
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
Sol1:Lazy标记
#include<cstdio> #define mid (s[x].l+s[x].r>>1) int n,m,d[100001]; char p[3]; struct oo{int l,r;long long lazy,sum;}s[400001]; void build(int x,int l,int r) { s[x].l=l,s[x].r=r; if(l==r){s[x].sum=d[l];return ;} build(x<<1,l,mid);build(x<<1|1,mid+1,r); s[x].sum=s[x<<1].sum+s[x<<1|1].sum; } void pushdown(int x) //lazy标记下传 { int l=x<<1,r=x<<1|1; s[l].sum+=(s[l].r-s[l].l+1)*s[x].lazy; s[r].sum+=(s[r].r-s[r].l+1)*s[x].lazy; s[l].lazy+=s[x].lazy;s[r].lazy+=s[x].lazy; s[x].lazy=0; } void change(int x,int l,int r,int v) { if(l<=s[x].l&&r>=s[x].r) { s[x].sum+=(s[x].r-s[x].l+1)*v; s[x].lazy+=v; return ; } if(s[x].lazy) //记得标记下传 pushdown(x); if(l<=mid) change(x<<1,l,r,v); if(r>mid) change(x<<1|1,l,r,v); s[x].sum=s[x<<1].sum+s[x<<1|1].sum; } long long get(int x,int l,int r) { if(l<=s[x].l&&r>=s[x].r) return s[x].sum; if(s[x].lazy) //记得标记下传 pushdown(x); long long ans=0; if(l<=mid)ans+=get(x<<1,l,r); if(r>mid)ans+=get(x<<1|1,l,r); s[x].sum=s[x<<1].sum+s[x<<1|1].sum; return ans; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%d",&d[i]); build(1,1,n);int x,y,z; while(m--) { scanf("%s",p+1); if(p[1]==‘Q‘) { scanf("%d%d",&x,&y); printf("%lld ",get(1,x,y)); } else { scanf("%d%d%d",&x,&y,&z); change(1,x,y,z); } } }
所谓标记永久化就是不下传标记,让标记永远都待在当前节点上。
对于修改操作,假如我们要修改[l,r],那么我们要把所有包含[l,r]的区间统计的信息都更改为受到标记影响后的,对于所有被[l,r]包含的第一级区间(就是递归时被包含不细分下去)我们就打上标记,更新信息。
为什么要一路修改下来而不是像普通线段树那样一路updata上去呢?因为假如用updata的话,有可能会被明明被标记影响了,但是由于不是第一级区间所以没有对它进行更改的结点向上更新出错误的信息。
对于询问,我们只需要把从根到我们要询问的区间这一路上所有的标记影响全部累加起来,统计答案再加上去即可。这样子,对于所有的区间,覆盖它的标记都逃不掉。
用区间加区间询问LG3372 为例,细细品味上面的话加上下面的代码,相信你能学会标记永久化的
#include <cstdio> #include <algorithm> using namespace std; typedef long long ll; const int maxn=1e5+5; int n,m; ll a[maxn]; struct segment_tree { ll sum[maxn<<2],add[maxn<<2]; void updata(int p) { sum[p]=sum[p<<1]+sum[p<<1|1]; } void build(int p,int l,int r) { if(l==r) {sum[p]=a[l];return;} int mid=(l+r)>>1; build(p<<1,l,mid); build(p<<1|1,mid+1,r); updata(p); } void change(int p,int l,int r,int L,int R,ll v) { sum[p]+=v*(R-L+1); if(L<=l&&r<=R) {add[p]+=v;return;} int mid=(l+r)>>1; if(R<=mid)change(p<<1,l,mid,L,R,v); else if(L>mid)change(p<<1|1,mid+1,r,L,R,v); else change(p<<1,l,mid,L,mid,v),change(p<<1|1,mid+1,r,mid+1,R,v); } ll query(int p,int l,int r,int L,int R) { if(L<=l&&r<=R)return sum[p]; int mid=(l+r)>>1;ll res=add[p]*(R-L+1); if(R<=mid)res+=query(p<<1,l,mid,L,R); else if(L>mid)res+=query(p<<1|1,mid+1,r,L,R); else res+=query(p<<1,l,mid,L,mid)+query(p<<1|1,mid+1,r,mid+1,R); return res; } }T; int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;i++) scanf("%lld",a+i); T.build(1,1,n); for(int i=1;i<=m;i++) { int opt,l,r;ll v; scanf("%d%d%d",&opt,&l,&r); if(opt==1) { scanf("%lld",&v); T.change(1,1,n,l,r,v); } else printf("%lld ",T.query(1,1,n,l,r)); } return 0; }
以上是关于Pku3468 A Simple Problem with Integers的主要内容,如果未能解决你的问题,请参考以下文章
BZOJ3212: Pku3468 A Simple Problem with Integers
bzoj3212: Pku3468 A Simple Problem with Integers(线段树)
BZOJ3212: Pku3468 A Simple Problem with Integers
PKU-3468 simple problem with integer 线段树 LL坑点(欢迎讨论)