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.
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.
大意
给你一个序列,如果输入Q,a,b则你需要查询区间a到b的和并输出。若输入的是C,a,b,c则你需要把2区间a到b的数都加上c。
解法
可以建立线段树,每个结点代表对应区间的和。建树的时候,记得把数组开的大于4*100000。
这道题考察的是区间修改和区间查询。用到了懒惰标记。值得的注意的是:(单点修改,区间询问)
和(区间修改,区间询问)是不同的题型。
代码
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 100010
#define ll long long
using namespace std;
//线段树大小要开到4n,2n有能会超出,亲身经历过。唉,那真是一个悲伤的故事
ll sum[4*N],lazy[4*N],a[4*N];
void build(int k,int l,int r) //建树
{
if(l==r)
{
sum[k]=a[l]; //等到了叶节点时,l的值正好与序列id对应
return;
}
int mid=l+r>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
sum[k]=sum[k<<1]+sum[k<<1|1];
}
void add(int k,int l,int r,int v) //整个区间加上v并且标记
{
lazy[k]+=v;
sum[k]+=(ll)v*(r-l+1); //这个add函数的作用就是标记并更新k节点
}
void push(int k,int l,int r,int mid) //push函数的作用就是标记并更新当前节点的左右儿子结点,然后取消当前节点的标记
{
if(lazy[k]) //正如其名,将标记向下推
{
add(k<<1,l,mid,lazy[k]);
add(k<<1|1,mid+1,r,lazy[k]);
lazy[k]=0;
}
}
void modify(int k,int l,int r,int x,int y,int v) //函数作用:更新区间[X,Y],给区间中的数加上v
{
if(l>=x&&r<=y) return add(k,l,r,v);
int mid=l+r>>1;
push(k,l,r,mid); //注意,没达到一个节点都要下传标记
if(x<=mid)modify(k<<1,l,mid,x,y,v);
if(y>mid)modify(k<<1|1,mid+1,r,x,y,v);
sum[k]=sum[k<<1]+sum[k<<1|1]; //下传后,更新当前sum值,保证sum[k]的正确性
}
ll query(int k,int l,int r,int x,int y) //函数作用:查询区间[x,y]的区间和
{
if(l>=x&&r<=y)return sum[k];
int mid=l+r>>1;
ll res=0;
push(k,l,r,mid); //记得下传标记
if(x<=mid)res+=query(k<<1,l,mid,x,y);
if(y>mid)res+=query(k<<1|1,mid+1,r,x,y);
//c此处不需要更新区间和,因为sum没有修改
return res;
}
int main()
{
int n,m,i,j;
while(~scanf("%d%d",&n,&m))
{
char c;
int t1,t2,t3;
memset(lazy,0,sizeof(lazy));
memset(sum,0,sizeof(sum));
for(i=1;i<=n;i++)
scanf("%lld",&a[i]);
build(1,1,n); //建树!千万别忘了.....唉,这背后又是一个悲伤的故事
for(j=1;j<=m;j++)
{
getchar(); //注意输入的是字符,加getchar();
scanf("%c",&c);
if(c=='Q')
{
scanf("%d%d",&t1,&t2);
printf("%lld\n",query(1,1,n,t1,t2));
}
if(c=='C')
{
scanf("%d%d%d",&t1,&t2,&t3);
modify(1,1,n,t1,t2,t3);
}
}
}
return 0;
}