OpenJudge 线段树练习题 A Simple Problem with Integers
题目描述
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
提示
The sums may exceed the range of 32-bit integers.
分析
这个题比较简单,基本上就是线段树的修改和访问。题目提示sums的数值有可能超出32-bit的范围,所以我们将sum定义为long long型(long在32位机器中是4字节,不推荐)。对于程序输入的每次C操作,如果只是简单的对区间内的每一个点进行操作,会因为运算量大而TLE。解决办法是利用一个lazytag的标记,当某一区间被加c时,对整个区间进行加法操作,并将lazytag的值加上c。当需要访问该区间的子区间时,再将lazytag下传到子区间,并清除该区间的lazytag标记,这是一种标记思想。值得注意的是,在下传lazytag时,由于子区间的lazytag有可能已经有标记值,应该使用+=而不是=。
AC代码
#include <iostream>
using namespace std;
int A[100000]; //存N个数
struct node { //树结点
long long sum; //区间元素的和
long long lt; //lazytag
int a, b; //区间左右端点
int len; //区间长度
}tree[400000]; //存树
void buildtree(int i, int a, int b) //建树
{
tree[i].lt = 0;
tree[i].a = a;
tree[i].b = b;
if (a < b) //有子树
{
int mid = (a+b)/2, newi = 2*i;
buildtree(newi,a,mid); //建左子树
buildtree(newi+1,mid+1,b); //建右子树
tree[i].sum = tree[newi].sum+tree[newi+1].sum; //当前结点所代表区间的和
tree[i].len = tree[newi].len+tree[newi+1].len; //区间长度
}
else //树叶
{
tree[i].sum = (long long)A[a-1];
tree[i].len = 1;
}
}
void addc(int i, int a, int b, int & c) //区间[a,b]各元素加c
{
if (tree[i].a == a && tree[i].b == b)
{
tree[i].lt += c; //更新标记lazytag
tree[i].sum += c*tree[i].len; //区间元素加c后结果
return;
}
int newi = 2*i, mid = (tree[i].a+tree[i].b)/2;
if (tree[i].lt != 0) //lazytag下传,消除当前lazytag的标记
{
tree[newi].sum += tree[i].lt*tree[newi].len;
tree[newi+1].sum += tree[i].lt*tree[newi+1].len;
tree[newi].lt += tree[i].lt;
tree[newi+1].lt += tree[i].lt;
tree[i].lt = 0;
}
if (b <= mid) //[a,b]在左端
addc(newi,a,b,c);
else if (a > mid) //[a,b]在右端
addc(newi+1,a,b,c);
else //[a,b]横跨左右
{
addc(newi,a,tree[newi].b,c);
addc(newi+1,tree[newi+1].a,b,c);
}
tree[i].sum = tree[newi].sum+tree[newi+1].sum; //更新sum
}
long long askforsum(int i, int a, int b) //区间[a,b]元素的和
{
if (tree[i].a == a && tree[i].b == b) //区间[a,b]
return tree[i].sum;
int newi = 2*i, mid = (tree[i].a+tree[i].b)/2;
if (tree[i].lt != 0) //lazytag下传,消除当前lazytag的标记
{
tree[newi].sum += tree[i].lt*tree[newi].len;
tree[newi+1].sum += tree[i].lt*tree[newi+1].len;
tree[newi].lt += tree[i].lt;
tree[newi+1].lt += tree[i].lt;
tree[i].lt = 0;
}
if (b <= mid) //[a,b]在左端
return askforsum(newi,a,b);
else if (a > mid) //[a,b]在右端
return askforsum(newi+1,a,b);
else //[a,b]横跨左右
return askforsum(newi,a,tree[newi].b)+askforsum(newi+1,tree[newi+1].a,b);
}
int main()
{
int N, Q, a, b, c, i;
char operation; //操作(C或Q)
scanf("%d%d",&N,&Q); //输入N、Q
for (i = 0; i < N; ++i) //N个数
scanf("%d",A+i);
buildtree(1,1,N); //建树
for (i = 0; i < Q; ++i) //Q次操作
{
scanf("%c",&operation); //吃掉回车
scanf("%c%d%d",&operation,&a,&b);
if (operation == 'C') //操作C
{
scanf("%d",&c);
addc(1,a,b,c);
}
else //操作Q
printf("%lld\n",askforsum(1,a,b));
}
return 0;
}