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
此题除了用到了线段树的基本性质(求和)以外,还用到了区间修改。主要是加入了延迟标记缩短对区间修改的时间,十分便利。本题也是懂了如何利用此工具以后就变成一道不难的题(但是我连工具也不会用啊……)
为了加强理解,强迫自己写了注释,思路就不写了,都放在注释里。
用G++交会超时,C++的话连上限5s的一半时间都没达到……
#include <algorithm> #include <iostream> #include <sstream> #include <cstring> #include <cstdlib> #include <string> #include <vector> #include <cstdio> #include <cmath> #include <queue> #include <stack> #include <map> #include <set> using namespace std; #define INF 0x3f3f3f3 struct Node{ long long sum,val; }node[400005]; void pushup(int tr){ node[tr].sum=node[tr*2].sum+node[tr*2+1].sum; } void pushdown(int tr,int m){ if (node[tr].val) { node[tr*2].val+=node[tr].val; node[tr*2+1].val+=node[tr].val; node[tr*2].sum+=(long long)(m-m/2)*node[tr].val; node[tr*2+1].sum+=(long long)m/2*node[tr].val; node[tr].val=0; } } void build(int l,int r,int tr){//递归构造线段树,默认延迟标记为0 node[tr].val=0; if (l==r) { scanf("%lld",&node[tr].sum); return ; } int m=(l+r)/2; build(l, m, tr*2); build(m+1, r, tr*2+1); pushup(tr);//区间求和,每个节点表示的就是该区域内之和。 } long long query(int L,int R,int l,int r,int tr){//L,R分别需要查询的区间的左右界限。 if (L<=l&&r<=R) { return node[tr].sum;//此时查询区间大于该节点区间,因此加上该节点区间和 } int m=(l+r)/2; pushdown(tr, r-l+1);//将延迟标记向下传递,并将相应的和的变化反应到子节点上。之后对子节点区间求和时即可将延迟标记代表的和算上。 long long ans=0; //已知此时l与r必然存在l<L或r>R,即l,r此时不被查询区间包括 if (L<=m) { //查询区间的左边界小于m时,将查询区间放到l到m区间内进行求和。 ans+=query(L,R,l,m,tr*2); } if (m<R) { //同上,通过这两步,即可完成求和工作。 ans+=query(L, R, m+1, r, tr*2+1); pushup(tr); } return ans; } void update(int L,int R,int add,int l,int r,int tr){//区间修改的重头戏,add为修改的大小 if(L<=l&&r<=R)//修改区间大于该节点代表的区间时,算上add所改变的和的大小,并相应修改延迟标记。 { node[tr].sum+=(long long)add*(r-l+1); node[tr].val+=add; return ; } pushdown(tr,r-l+1);//同上,将延迟标记传递。 int m=(l+r)/2; if(L<=m)update(L,R,add,l,m,tr*2);//递归修改,同上,不赘述了,与query函数内涵相同。 if(m<R)update(L,R,add,m+1,r,tr*2+1); pushup(tr); } int main() { int n,q,a,b; int c; while(scanf("%d%d",&n,&q)!=EOF) { build(1,n,1);//构造子树 char s[3]; while(q--) { scanf("%s",s); if(s[0]=='Q') { scanf("%d%d",&a,&b); printf("%lld\n",query(a, b, 1, n, 1)); } else if(s[0]=='C') { scanf("%d%d%d",&a,&b,&c); update(a,b,c,1,n,1); } } } return 0; }