思路:使用线段树,并且采用延迟标记的方法使更新操作的复杂度控制在O(logn)。这里采用数组的方法存储二叉树,可以省下两个指针的空间,但代码变得不是很直观,而且容易犯错。
另外此题中数据可能超过32位,故使用long long类型存储(POJ平台不能采用__int64,具体原因参见这里),同时使用%lld输出。另外,直接使用scanf读入一个字符的方法会读入空白字符,这里使用scanf("%1s",c);实现。注意因为%s会在输入字符串的末尾加上终止符'\0',故c至少是一个长度为2的数组。
6596kB | 600ms | 3338 B |
#include <stdio.h> struct InTree{ int l,u; long long v;//使用long long 和输入输入的%lld来保证数据超过32位仍能正常运行 long long add;//add为延迟标记,标识它的子结点中的每个元素需要增加add }; InTree tree[3*100000]; //最多有2N-1个节点,最底层最多有N个节点,所以最多占用3N-2个位置。使用tree数组的1~3N-1位置。 //A是输入的数据,i是当前节点在tree数组中的位置,l和u是上下界 void BuildInTree(int *A,int i,int low, int upp){ tree[i].add = 0; if(low==upp){ tree[i].l = tree[i].u = low; tree[i].v = A[low]; return; } else{ tree[i].l = low; tree[i].u = upp; BuildInTree(A,2*i,low,(low+upp)/2);//左子树的位置为2*i BuildInTree(A,2*i+1,(low+upp)/2+1,upp);//右子树的位置为2*i+1 tree[i].v = tree[2*i].v + tree[2*i+1].v; } } //更新l到u区间的叶节点值加上v,tree[i]为当前节点,在这里采用延迟标记的方法 void Update(int i,int low, int upp, long long v){ if(low <= tree[i].l && tree[i].u <= upp){ tree[i].v += (tree[i].u-tree[i].l+1)*v;//查询到本节点,更新数据 tree[i].add += v;//更新标记 } else{ //更新tree[i]的左右的左右子树的标记 if(tree[i].add !=0){ //先把标记应用到子结点,再更新子结点的标记 tree[2*i].v += (tree[2*i].u-tree[2*i].l+1)* tree[i].add; tree[2*i].add += tree[i].add; tree[2*i+1].v += (tree[2*i+1].u-tree[2*i+1].l+1)* tree[i].add; tree[2*i+1].add += tree[i].add; tree[i].add = 0;//将本节点的标记置0 } if(low<=(tree[i].l+tree[i].u)/2){ Update(2*i,low,upp,v); } if(upp>=(tree[i].l+tree[i].u)/2+1){ Update(2*i+1,low,upp,v); } tree[i].v = tree[2*i].v + tree[2*i+1].v; } } //查询l到u之间的数据和,tree[i]为当前节点 long long Query(int i,int low, int upp){ if(low <= tree[i].l && tree[i].u <= upp){ return tree[i].v; } else{ //更新tree[i]的左右的左右子树的标记 if(tree[i].add !=0){ //先把标记应用到子结点,再更新子结点的标记 tree[2*i].v += (tree[2*i].u-tree[2*i].l+1)* tree[i].add; tree[2*i].add += tree[i].add; tree[2*i+1].v += (tree[2*i+1].u-tree[2*i+1].l+1)* tree[i].add; tree[2*i+1].add += tree[i].add; tree[i].add = 0;//将本节点的标记置0 } long long val = 0; if(low<=(tree[i].l+tree[i].u)/2){ val += Query(2*i,low,upp); } if(upp>=(tree[i].l+tree[i].u)/2+1){ val += Query(2*i+1,low,upp); } return val; } } int main(){ int N,Q; scanf("%d %d", &N, &Q); int *A = new int[N+1];//只使用1~N的区间 for(int i=1;i<=N;i++)scanf("%d", A+i); BuildInTree(A,1,1,N); char c[2]; int l,u; long long add; for(int i=0;i<Q;i++){ scanf("%1s",c);//读入一个非空白的字符,注意留空间给%s末尾的'\0' if(c[0]=='Q'){ scanf("%d %d",&l,&u); printf("%lld\n",Query(1,l,u)); } else if(c[0]=='C'){ scanf("%d %d %lld",&l,&u,&add); Update(1,l,u,add); } } return 0; }3439:A Simple Problem with Integers
查看 提交 统计 提示 提问
时间限制: 5000ms 内存限制: 65536kB
描述
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.
输入
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.
输出
You need to answer all Q commands in order. One answer in a line.
样例输入
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
样例输出
4
55
9
15