问题:
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.
题意:给出了一个序列,你需要处理如下两种询问。"C a b c"表示给[a, b]区间中的值全部增加c。"Q a b" 询问[a, b]区间中所有值的和。
思路一:线段树的模板。
代码一:
#include<stdio.h> #include<string.h> #include<algorithm> #define M 100010 #define mem(a) memset(a,0,sizeof(a)); using namespace std; long long sum[4*M];//该数组表示区间和 long long add[4*M];//该数组进行懒惰标记 int n,m; void build(int left,int right,int root)//进行建树 { if(left==right)//如果此点是叶子节点则进行赋值给这个节点 { scanf("%lld",&sum[root]); return ; } int mid=(left+right)>>1; build(left,mid,root<<1);//建造左子树 build(mid+1,right,root<<1|1);//建造右子树 sum[root]=sum[root<<1]+sum[root<<1|1];//建造该点的区间和 } void pushdown(int root,int left)//懒惰标记 { if(add[root]!=0) { add[root<<1]+=add[root];//左儿子的懒惰值加上父亲的懒惰值 add[root<<1|1]+=add[root];//右儿子的懒惰值加上父亲的懒惰值 sum[root<<1]+=add[root]*(left-(left>>1));//更新左节点的区间和 sum[root<<1|1]+=add[root]*(left>>1);//更新右节点的区间和 add[root]=0;//父亲节已经用过了。将父亲节点清空 } } void updata(int x,int y,int left,int right,int root,int c)//进行区间更新 { if(left>=x&&right<=y)//如果这个区间被完全包括在目标区间里面,更新该节点的区间和 { sum[root]=sum[root]+(right-left+1)*c; add[root]=add[root]+c; return ; } pushdown(root,right-left+1);//懒惰标记 int mid=(right+left)>>1; if(x<=mid)//如果这个区间的左儿子和目标区间有交集,那么搜索左儿子 updata(x,y,left,mid,root<<1,c); if(y>mid)//如果这个区间的右儿子和目标区间有交集,那么搜索右儿子 updata(x,y,mid+1,right,root<<1|1,c); sum[root]=sum[root<<1]+sum[root<<1|1];//该节点的区间和等于左儿子的区间和加上右儿子的区间和 } long long query(int x,int y,int left,int right,int root)//进行区间查询 { if(left>=x&&right<=y)//如果这个区间被完全包括在目标区间里面,直接返回这个区间的值 return sum[root]; pushdown(root,right-left+1);//也需要进行这个懒惰标记 int mid=(right+left)>>1; long long ans=0; if(x<=mid)//如果这个区间的左儿子和目标区间有交集,那么搜索左儿子 ans+=query(x,y,left,mid,root<<1); if(y>mid)//如果这个区间的右儿子和目标区间有交集,那么搜索右儿子 ans+=query(x,y,mid+1,right,root<<1|1); return ans; } int main() { while(~scanf("%d%d",&n,&m)) { mem(sum);//清空 mem(add);//清空 build(1,n,1);//进行建树 char s[10]; for(int i=1; i<=m; i++) { int left,right,root; scanf("%s",s); if(s[0]=='Q')//进行查询 { scanf("%d%d",&left,&right); long long hh=query(left,right,1,n,1); printf("%lld\n",hh); } else//进行更新 { scanf("%d%d%d",&left,&right,&root); updata(left,right,1,n,1,root); } } } }
思路二:树状数组模板。
代码二:
#include<stdio.h> #include<string.h> #include<algorithm> using namespace std; long long sum1[500010],sum[500010],n,m,a[500010]; int kkk(int i) { return i&(-i); } void update(int x,long long y) { int u=x; while(x<=n) { sum[x]+=y; sum1[x]+=y*(u-1); x+=kkk(x); } } long long getsum(int x) { long long ans=0,u=x; while(x>0) { ans=ans+sum[x]*u-sum1[x]; x-=kkk(x); } return ans; } int main() { while(~scanf("%d%d",&n,&m)) { memset(sum,0,sizeof(sum)); memset(sum1,0,sizeof(sum1)); a[0]=0; for(int i=1; i<=n; i++) { scanf("%lld",&a[i]); update(i,a[i]-a[i-1]); } char s[10]; for(int i=1;i<=m;i++) { scanf("%s",s); int x,y,z; if(s[0]=='Q') { scanf("%d%d",&x,&y); long long h=getsum(y)-getsum(x-1); printf("%lld\n",h); } else if(s[0]=='C') { scanf("%d%d%d",&x,&y,&z); update(x,z); update(y+1,-z); } } } return 0; }