线段树区间修改I
1、update(L,R,V):把区间[L,R]之间的数全部增加V;
2、query(L,R)查找区间[L,R]的所有元素和sum,最大值_Max,最小值_Min,详见代码。
Code:
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long LL;
#define MAX 110000
LL n,m;
LL _Sum,_Max,_Min;
struct Tree
{
LL L,R;
LL Sum,add,Min,Max;
}trees[MAX*3];
void pushup(LL x)//向上维护x节点的信息
{
trees[x].Sum = trees[x<<1].Sum+trees[x<<1|1].Sum;
trees[x].Max = max(trees[x<<1].Max,trees[x<<1|1].Max);
trees[x].Min = min(trees[x<<1].Min,trees[x<<1|1].Min);
}
void pushdown(LL x)//分解x节点的add值,将add值传给x节点的所有子节点
{
LL tmp = 2*x;
trees[tmp].add +=trees[x].add;
trees[tmp+1].add +=trees[x].add;
trees[tmp].Sum +=trees[x].add*(trees[tmp].R-trees[tmp].L+1);
trees[tmp+1].Sum +=trees[x].add*(trees[tmp+1].R-trees[tmp+1].L+1);
trees[tmp].Max +=trees[x].add; trees[tmp+1].Max +=trees[x].add;
trees[tmp+1].Min +=trees[x].add; trees[tmp+1].Min +=trees[x].add;
trees[x].add = 0;//最后将x节点的add赋为0
}
void build(int L,int R,int x)//建立树
{
trees[x].L = L,trees[x].R = R;
trees[x].add = 0;
if(L==R)//L、R相等时证明此时到达叶子节点
{
scanf("%lld",&trees[x].Sum);//输入各叶子节点值
trees[x].Max = trees[x].Sum;
trees[x].Min = trees[x].Sum;
return;
}
int tmp = x<<1;
int mid = (L+R)>>1;
build(L,mid,tmp);
build(mid+1,R,tmp+1);
pushup(x);//向上维护x节点的信息
}
void update(LL L,LL R,LL c,LL x)//修改区间信息
{
if(R<trees[x].L || L>trees[x].R) return;//当所修改区间超出树的区间范围内直接跳出
if(L<=trees[x].L&&trees[x].R<=R)//在x节点的区间范围内时,直接修改节点信息
{
trees[x].add +=c;
trees[x].Sum +=c*(trees[x].R-trees[x].L+1);
trees[x].Max +=c, trees[x].Min +=c;
return;
}
if(trees[x].add) pushdown(x);//在树的范围内但不在节点x的范围内此时先将x节点的add值分解即传给子节点
LL tmp = x<<1;
update(L,R,c,tmp);//然后左右进行递归
update(L,R,c,tmp+1);
pushup(x);//每次递归完成后都应该维护节点x的信息
}
void query(LL L,LL R,LL x)//查询区间信息
{
if(R<trees[x].L || L>trees[x].R) return;//查询区间不在有效区间内时直接跳出
if(L<=trees[x].L && trees[x].R <= R)//查询区间在节点x的区间范围内时直接获取所需信息
{
_Sum +=trees[x].Sum;
_Min = min(_Min,trees[x].Min);
_Max = max(_Max,trees[x].Max);
return;
}
if(trees[x].add) pushdown(x);//未能准确查出区间,则先将x节点的add值进行分解
LL tmp = x<<1;
LL mid = (trees[x].L+trees[x].R)>>1;//然后确定查找区间的确切位置
if(R<mid)
query(L,R,tmp);
else if(L>mid)
query(L,R,tmp+1);
else
{
query(L,mid,tmp);
query(mid+1,R,tmp+1);
}
}
int main()
{
while(~scanf("%lld%lld",&n,&m))
{
build(1,n,1);
int choose;
while(m--)
{
scanf("%d",&choose);
if(choose==0)
{
LL L,R;
scanf("%lld%lld",&L,&R);
_Sum = 0,_Max = 0,_Min = MAX;
//_Sum = 0;
query(L,R,1);
printf("%lld %lld %lld\n",_Sum,_Max,_Min);
}
else
{
LL L,R,c;
scanf("%lld%lld%lld",&L,&R,&c);
update(L,R,c,1);
}
}
}
return 0;
}
线段树区间修改II:
1、update(L,R,V):把区间[L,R]之间的数全部改为V;
2、query(L,R)查找区间[L,R]的所有元素和sum,最大值_Max,最小值_Min,详见代码。
Code:
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long LL;
#define MAX 110000
LL n,m;
LL _Sum,_Max,_Min;
struct Tree
{
LL L,R;
LL Sum,Set,Min,Max;
}trees[MAX*3];
void pushup(LL x)//向上维护x节点的信息
{
trees[x].Sum = trees[x<<1].Sum+trees[x<<1|1].Sum;
trees[x].Max = max(trees[x<<1].Max,trees[x<<1|1].Max);
trees[x].Min = min(trees[x<<1].Min,trees[x<<1|1].Min);
}
void pushdown(LL x)//分解x节点的add值,将add值传给x节点的所有子节点
{
LL tmp = 2*x;
trees[tmp].Set =trees[x].Set;
trees[tmp+1].Set =trees[x].Set;
trees[tmp].Sum =trees[x].Set*(trees[tmp].R-trees[tmp].L+1);
trees[tmp+1].Sum =trees[x].Set*(trees[tmp+1].R-trees[tmp+1].L+1);
trees[tmp].Max =trees[x].Set; trees[tmp+1].Max =trees[x].Set;
trees[tmp+1].Min =trees[x].Set; trees[tmp+1].Min =trees[x].Set;
trees[x].Set = 0;//最后将x节点的add赋为0
}
void build(int L,int R,int x)//建立树
{
trees[x].L = L,trees[x].R = R;
trees[x].Set = 0;
if(L==R)//L、R相等时证明此时到达叶子节点
{
scanf("%lld",&trees[x].Sum);//输入各叶子节点值
trees[x].Max = trees[x].Sum;
trees[x].Min = trees[x].Sum;
return;
}
int tmp = x<<1;
int mid = (L+R)>>1;
build(L,mid,tmp);
build(mid+1,R,tmp+1);
pushup(x);//向上维护x节点的信息
}
void update(LL L,LL R,LL c,LL x)//修改区间信息
{
if(R<trees[x].L || L>trees[x].R) return;//当所修改区间超出树的区间范围内直接跳出
if(L<=trees[x].L&&trees[x].R<=R)//在x节点的区间范围内时,直接修改节点信息
{
trees[x].Set =c;
trees[x].Sum =c*(trees[x].R-trees[x].L+1);
trees[x].Max =c, trees[x].Min =c;
return;
}
if(trees[x].Set) pushdown(x);//在树的范围内但不在节点x的范围内此时先将x节点的add值分解即传给子节点
LL tmp = x<<1;
update(L,R,c,tmp);//然后左右进行递归
update(L,R,c,tmp+1);
pushup(x);//每次递归完成后都应该维护节点x的信息
}
void query(LL L,LL R,LL x)//查询区间信息
{
if(R<trees[x].L || L>trees[x].R) return;//查询区间不在有效区间内时直接跳出
if(L<=trees[x].L && trees[x].R <= R)//查询区间在节点x的区间范围内时直接获取所需信息
{
_Sum +=trees[x].Sum;
_Min = min(_Min,trees[x].Min);
_Max = max(_Max,trees[x].Max);
return;
}
if(trees[x].Set) pushdown(x);//未能准确查出区间,则先将x节点的add值进行分解
LL tmp = x<<1;
LL mid = (trees[x].L+trees[x].R)>>1;//然后确定查找区间的确切位置
if(R<mid)
query(L,R,tmp);
else if(L>mid)
query(L,R,tmp+1);
else
{
query(L,mid,tmp);
query(mid+1,R,tmp+1);
}
}
int main()
{
while(~scanf("%lld%lld",&n,&m))
{
build(1,n,1);
int choose;
while(m--)
{
scanf("%d",&choose);
if(choose==0)
{
LL L,R;
scanf("%lld%lld",&L,&R);
_Sum = 0,_Max = 0,_Min = MAX;
//_Sum = 0;
query(L,R,1);
printf("%lld %lld %lld\n",_Sum,_Max,_Min);
}
else
{
LL L,R,c;
scanf("%lld%lld%lld",&L,&R,&c);
update(L,R,c,1);
}
}
}
return 0;
}
其实以上两份代码大致相同,只是将其中的部分进行了更改,即将第一份代码的pushdown()和update()中的+=改为=,也就是将累加改为了直接赋值。