一、在区间修改的基础上:区间查询,区间修改
B - 一棵普通的线段树
出题人明天就要半期考试了,课程是《火葬场与波》.
出题人倒在血泊中,一双有力的手摇晃着出题人的肩膀:
“同志,醒醒,你还有题没出完呢”.
以下是他的遗言:
给你一个数组 A[1..n]A[1..n],初始值全为 0.
你需要写一棵裸的区间修改、区间查询的线段树,
以支持两个操作.
第一个操作是对区间 [L,R][L,R] 内的数每个数加上 vv.
第二个操作是给出区间 [L,R][L,R] 内所有数的和.
Input
第一行包含两个整数 n(1≤n≤106)n(1≤n≤106) 和 m(1≤m≤106)m(1≤m≤106),
分别是数组的大小和操作的个数.
接下来 mm 行,每行四个用空格分隔的整数 o l r v (1≤l≤r≤n,|v|≤103)o l r v (1≤l≤r≤n,|v|≤103).
如果 o=0o=0,则表示对区间 [l,r][l,r] 内每个数都加上 vv.
否则,请给出区间 [l,r][l,r] 内所有数的和,此时 v≡0v≡0.
Output
对于每个 o≠0o≠0 的操作,
输出包含一个整数的一行,表示对应区间内所有数的和.
Sample Input
5 4
0 2 4 5
1 3 5 0
0 1 3 -2
1 1 5 0
Sample Output
10
9
Hint
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
int n;
long long c1[1000001],c2[1000001];
long long lowbit(int x)
{
return x&(-x);
}
long long getsum(long long x)//区间查询
{
long long sum1=0,sum2=0,i=x;
while(x)
{
sum1+=c1[x];
sum2+=c2[x];
x-=lowbit(x);
}
return (i+1)*sum1-sum2;
}
void update(long long x,long long val)//区间修改
{
long long i=x;
while(x<=n)
{
c1[x]+=val;
c2[x]+=i*val;
x+=lowbit(x);
}
}
int main()
{
ios::sync_with_stdio(0);
int m;
cin>>n>>m;
int o,l,r,v;
while(m--)
{
cin>>o>>l>>r>>v;
if(o==0)
{
update(l,v);//(l,v)加v之后差分数组c[]中c[l]比c[l-1]大v
update(r+1,-v);//c[r+1]比c[r]小v
}
else{
cout<<getsum(r)-getsum(l-1)<<endl;//sum[l...r]=sum[r]-sum[l-1]前缀和
}
}
return 0;
}
二、在单点修改的基础上:单点查询,区间查询
区间求和:最简单的树状数组操作,利用树状数组的区域管理的操作,能够迅速地求出的[1,x]的前缀和,所以我们求[l,r]的和的时候,只需要求出[1,r],[1,l-1],所以[l,r]的和=[1,r]-[1,l-1].
单点修改:从该点x到最后一位的点(n)所管辖的区间所有都要修改,所以操作为x+=lowbit(x)就能得到所有的x被管辖的位置。
int getsum(int x)
{
int sum=0;
while(x)
{
sum+=c[x];
x-=lowbit(x);
}
return sum;
}//区间求和为sum[l...r]=getsum(r)-getsum(l-1)...............单点查询为a[x]=getsum(x)
void update(int x,int val)
{
while(x<=n)
{
c[x]+=val;
x+=lowbit(x);
}
}//区间修改为update(l,val),update(r+1,-val)..............update(i,val),update(i+1,-val)
//单点修改为update(i,val)就可以了