UESTC 839——东风不与周郎便(线段树区间修改模板)

线段树区间修改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()中的+=改为=,也就是将累加改为了直接赋值。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值