poj 3468 A Simple Problem with Integers(线段树区间lazy标记修改or树状数组)
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.
解题思路:题意很清楚,大区间修改和询问:线段树+lazy懒标记,or树状数组,详解在代码里,注意求和用long long类型!
AC代码一之线段树懒标记(2500ms):
1 #include<string.h> 2 #include<cstdio> 3 using namespace std; 4 typedef long long LL; 5 const int maxn=100005; 6 int n,q,a,b;LL c,s[maxn],lazy[maxn<<2],sum[maxn<<2];char ch; 7 void build(int l,int r,int x){//建树基本操作 8 int mid=(l+r)>>1; 9 if(l==r){sum[x]=s[mid];return;} 10 build(l,mid,x<<1); 11 build(mid+1,r,x<<1|1); 12 sum[x]=sum[x<<1]+sum[x<<1|1]; 13 } 14 void push_down(int x,int len){//下放懒标记 15 if(lazy[x]){ 16 lazy[x<<1]+=lazy[x];//延迟修改量是叠加的,沿着子树可以继续更新下去 17 lazy[x<<1|1]+=lazy[x]; 18 sum[x<<1]+=(LL)(len-(len>>1))*lazy[x];//更新左子节点的值:加上左区间长度乘上父节点的懒标记 19 sum[x<<1|1]+=(LL)(len>>1)*lazy[x];//更新右子节点的值:加上右区间长度乘上父节点的懒标记 20 lazy[x]=0;//同时标记父节点已经修改完成,即置懒标记为0 21 } 22 } 23 void modify(int l,int r,int x,LL c){ 24 if(a<=l&&r<=b){//如果[a,b]包含了当前子区间[l,r],则直接进行懒标记,不再递归下去 25 lazy[x]+=c;//叠加懒标记值 26 sum[x]+=(LL)c*(r-l+1);//同时累加修改值乘上当前子区间的长度 27 return; 28 } 29 push_down(x,r-l+1);//如果修改区间不包含当前子区间,并且当前子区间有懒标记,则下放懒标记 30 int mid=(l+r)>>1; 31 if(b<=mid)modify(l,mid,x<<1,c); 32 else if(a>mid)modify(mid+1,r,x<<1|1,c); 33 else{ 34 modify(l,mid,x<<1,c); 35 modify(mid+1,r,x<<1|1,c); 36 } 37 sum[x]=sum[x<<1]+sum[x<<1|1];//修改某个区间后还要向上更新父节点的值 38 } 39 LL query(int l,int r,int x){ 40 if(a<=l&&r<=b)return sum[x];//如果访问的区间[a,b]包含子区间[l,r],直接返回返回当前区间的值 41 int mid=(l+r)>>1; 42 push_down(x,r-l+1);//如果不包含子区间,并且当前节点有被懒标记,则应下放懒标记,因为查询的区间可能更小(最小到叶子节点),为避免少计算,还要这步操作,此时就不用向上更新了,修改区间值才要 43 if(b<=mid)return query(l,mid,x<<1); 44 else if(a>mid)return query(mid+1,r,x<<1|1); 45 else return query(l,mid,x<<1)+query(mid+1,r,x<<1|1); 46 } 47 int main(){ 48 scanf("%d%d",&n,&q); 49 for(int i=1;i<=n;++i)scanf("%lld",&s[i]); 50 memset(lazy,0,sizeof(lazy));//注意:将每个节点的懒标记都标记为0 51 build(1,n,1);//建树 52 while(q--){ 53 getchar();//吃掉回车符避免对字符输入的影响 54 scanf("%c",&ch); 55 if(ch=='Q'){ 56 scanf("%d%d",&a,&b); 57 printf("%lld\n",query(1,n,1)); 58 } 59 else{ 60 scanf("%d%d%lld",&a,&b,&c); 61 modify(1,n,1,c); 62 } 63 } 64 return 0; 65 }
AC代码二之树状数组(2125ms):裸题,套一下树状数组区间查询和区间修改模板即可。
1 #include<string.h> 2 #include<cstdio> 3 typedef long long LL; 4 const int maxn=100005; 5 LL n,q,l,r,k,val[maxn],sum1[maxn],sum2[maxn];char op; 6 void add(LL *sum,LL x,LL val){ 7 while(x<=n){sum[x]+=val;x+=(x&-x);} 8 } 9 LL get_sum(LL *sum,LL x){ 10 LL ans=0; 11 while(x>0){ans+=sum[x];x-=(x&-x);} 12 return ans; 13 } 14 LL ask(LL x){ 15 return x*get_sum(sum1,x)-get_sum(sum2,x); 16 } 17 int main(){ 18 while(~scanf("%lld%lld",&n,&q)){ 19 memset(sum1,0,sizeof(sum1)); 20 memset(sum2,0,sizeof(sum2)); 21 memset(val,0,sizeof(val)); 22 for(LL i=1;i<=n;++i){ 23 scanf("%lld",&val[i]); 24 add(sum1,i,val[i]-val[i-1]);//维护差分数组 25 add(sum2,i,(i-1)*(val[i]-val[i-1])); 26 } 27 while(q--){ 28 getchar();//吸收回车符避免对单个字符读取的影响 29 scanf("%c",&op); 30 if(op=='C'){ 31 scanf("%lld%lld%lld",&l,&r,&k); 32 add(sum1,l,k),add(sum1,r+1,-k); 33 add(sum2,l,(l-1)*k);add(sum2,r+1,-r*k); 34 } 35 else{ 36 scanf("%lld%lld",&l,&r); 37 printf("%lld\n",ask(r)-ask(l-1));//区间查询[1,r]-[1,l-1]=[l,r] 38 } 39 } 40 } 41 return 0; 42 }