您的位置:首页 > 其它

[模板]-线段树-区间修改 + 区间查询

2017-09-03 22:05 405 查看
问题描述:

如题,已知一个数列,你需要进行下面两种操作:

1.将某区间每一个数加上x

2.求出某区间每一个数的和

代码:

long long st[100000 << 2],lazy[100000 << 2];//数据最多10w个,于是储存线段树的数组开4倍即40w,lazy为标记数组
long long a[100000];
void PushUp(int node)//更新结点信息,这里的功能是求和,node为结点编号
{
st[node] = st[node << 1] + st[(node << 1) | 1];//左儿子加右儿子
}
void BuildTree(int node,int l,int r)//建树,node为节点编号,其代表着区间[l,r]
{
if(l == r)//边界,区间内只有一个元素
{
st[node] = a[l];//直接赋值
return;
}
int mid = (l + r) / 2;//二分,注意位运算符号的方向!!!
BuildTree(node << 1,l,mid);//左儿子
BuildTree(node << 1 | 1,mid + 1,r);//右儿子
PushUp(node);//现在得到了左儿子和右儿子的值,由它们得到父亲node的值
}
void PushDown(int node,int ln,int rn)//下推标记的函数,ln为左儿子中元素的个数,rn为右儿子中元素的个数
{
if(lazy[node])//如果该结点带着标记
{
//下推标记
lazy[node << 1] += lazy[node];//左儿子
lazy[node << 1 | 1] += lazy[node];//右儿子
//修改儿子结点的值
st[node << 1] += ln * lazy[node];
st[node << 1 | 1] += rn * lazy[node];
lazy[node] = 0;//清除node结点的标记
}
}
void UpDate(int node,int l,int r,int a,int b,int c)//区间修改,[a,b]内的元素加c
{
if(a <= l && r <= b)//如果当前区间在区间[a,b]内
{
st[node] += c * (r - l + 1);//更新当前结点,向上保持正确
lazy[node] += c;//打上标记,表示当前区间的st正确,儿子区间的st仍需要根据lazy的值来调整
return;
}
int mid = (l + r) / 2;//二分
PushDown(node,mid - l + 1,r - mid);//下推标记
if(a <= mid)//如果左儿子代表的区间[l,mid]与[a,b]有重叠
UpDate(node << 1,l,mid,a,b,c);//递归求解
if(b > mid)//如果右儿子代表的区间[mid + 1,r]与[a,b]有重叠
UpDate(node << 1 | 1,mid + 1,r,a,b,c);//递归求解
PushUp(node);//更新当前结点node
}
long long Query(int node,int l,int r,long long a,long long b)//区间查询,结点node所代表的区间为[l,r]
{
if(a <= l && r <= b)//如果在区间内
return st[node];//直接返回
long long mid = (l + r) / 2;//二分
PushDown(node,mid - l + 1,r - mid);//下推标记
long long ans = 0;
if(a <= mid)//如果左儿子代表的区间[l,mid]与[a,b]有重叠
ans += Query(node << 1,l,mid,a,b);
if(b > mid)//如果右儿子代表的区间[mid + 1,r]与[a,b]有重叠
ans += Query(node << 1 | 1,mid + 1,r,a,b);
return ans;
}
int main()
{
int n,m;
//输入
cin >> n >> m;
for(int i = 1; i <= n; ++i)
scanf("%lld",&a[i]);
BuildTree(1,1,n);//先输入数列再建树!!!
for(int i = 1; i <= m; ++i)
{
long long order,x,y,k;
scanf("%lld",&order);
if(order == 1)
{
scanf("%lld%lld%lld",&x,&y,&k);
UpDate(1,1,n,x,y,k);//区间修改
}
else
{
scanf("%lld%lld",&x,&y);
printf("%lld\n",Query(1,1,n,x,y));//区间查询
}
}
return 0;
}


单点修改函数:

void UpDate(int node,int l,int r,int L,int C)//单点更新,结点编号为node,所代表区间为[l,r],需要在a[L]上加C
{
if(l == r)//边界,如果区间内只有一个元素,那么它一定是a[L]
{
st[node] += C;//加上
return;
}
int mid = (l + r) / 2;//二分
if(L <= mid)//如果a[L]在左儿子代表的区间
UpDate(node << 1,l,mid,L,C);
else
UpDate(node << 1 | 1,mid + 1,r,L,C);
PushUp(node);//向上更新(由左儿子和右儿子更新父亲)
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: