思路:使用线段树,并且采用延迟标记的方法使更新操作的复杂度控制在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