线段树

线段树将区间分为若干个子区间,子区间又继续分,直到区间为一个点(l==r)

对于父区间 [l,r] ,其子区间为 [l,(l+r)/2][(l+r)/2+1,r]

线段树一般用于求区间的值,如区间最值,区间求和等。

代码实现中, 假设节点下标从1开始,若某节点下标为x,那么其左子区间下标为2x,右子区间下标为2x+1,父节点下标为x/2;

在这里插入图片描述
常用宏定义

#define Mid ((l+r)>>1)      	//中间值   注意括号
#define lson rt<<1,l,Mid	//左结点
#define rson rt<<1|1,Mid+1,r	//右结点

建树

建树从根节点开始,递归建立左右子树,直到叶子节点,然后反向赋值,父节点的值=F(左节点的值,右节点的值),这个F根据题意确定,若求区间最大值则为max,若求区间和则为sum。

void build(int rt,int l,int r)      //建编号为rt的区间为[l,r]的树,主函数传进来的固定是(1,1,n)
{
    if(l==r){            //叶子结点赋初值,注意下标,Max的是编号,val原数组的是l,看图可以理解
        Max[rt] = val[l];
    }else{                      //建左右子树
        build(lson);
        build(rson);
        Max[rt] = max( Max[rt<<1], Max[rt<<1|1]);   //父结点Max值为Max(左子结点,右子结点)
    }
}

查询

查询为区间查询(只是查询某个点的话不需要线段树),即在区间里查询某个特性值,每次查询都是从跟结点开始往下,根据查询区间和当前区间的区间位置判断是要去左右子区间查询,还是直接返回。如果被查询区间是查询区间的子区间则直接返回子区间的值,如在[1,6]里查询[1,12]就返回[1,6]的值,不再往下查询。

void query(int rt,int l,int r,int L,int R)  //在[l,r]里查询[L,R]的值,[L,R]一直不变,[l,r]变
{
    if(L <= l && r <= R){   
        ans1 = max(ans1,Max[rt]);
        ans2 = min(ans2,Min[rt]);
    }else{
        if( L <= Mid) //查询区间在当前区间的左半区间有内容,如在[1,6]里查询[2,3]
            query(lson,L,R);    
        if( R > Mid)  //同理去右子区间,注意不能有else,因为有横跨左右的情况,如[1,6]里查询[2,5]
            query(rson,L,R);
    }
}

单点更新

void update(int rt,int l,int r,int pos,int num)
{
    if(l == r && r == pos){     //到对应的叶结点
        Max[rt] = num;
    }else{
        if( pos <= Mid)
            update(lson,pos,num);
        if( pos > Mid)          //或者直接else,点不可能同时在两个区间里
            update(rson,pos,num);
        Max[rt] = max( Max[rt<<1], Max[rt<<1|1]);
    }
}

区间更新


void pushDown(int rt,int len)
{
    add[rt<<1] = add[rt<<1|1] = add[rt];
    sum[rt<<1] = (len-(len>>1))*add[rt];
    sum[rt<<1|1] = (len>>1)*add[rt];
    add[rt] = 0;
}
 
void update(int rt,int l,int r,int L,int R,int z)
{
    if(L <= l && r <= R){
        add[rt] = z;//或 add[rt]+=z; 根据题意而定
        sum[rt] = (r-l+1)*add[rt];
    }else{
        if(add[rt])
            pushDown(rt,r-l+1);
        if(L <= Mid)
            update(lson,L,R,z);
        if(R > Mid)
            update(rson,L,R,z);
        sum[rt] = sum[rt<<1] + sum[rt<<1|1];
    }
}

例题

HDU 1166 敌兵布阵

#include <iostream>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstdio>
#include <map>

using namespace std;
#define mem(a, b) memset(a, b, sizeof(a))
#define PI acos(-1)

typedef long long ll;
int dir8[8][2] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}, {1, 1}, {1, -1}, {-1, 1}, {-1, -1}};
int dir4[4][2] = {1, 0, 0, 1, -1, 0, 0, -1};
const int INF = 0x3f3f3f3fll;
const long long intF = 0x3f3f3f3f3f3f3f3fll;
const int MAXn = 1e5 + 5;
const int mod = 1e9 + 7;

struct node
{
    int l, r, w, flag;
    int mid() { return (r + l) / 2; }
    int dis() { return r - l + 1; }
} a[MAXn * 4];

void build(int k, int l, int r)
{
    a[k].l=l;
    a[k].r=r;
    a[k].w=0;
    a[k].flag=0;
    if (l == r)
    {
        cin >> a[k].w;
        return;
    }
    build(k << 1, l ,a[k].mid());
    build(k<<1|1, a[k].mid()+1, r );
    a[k].w=a[k<<1].w+a[k<<1|1].w;
}

void down(int k)
{
    a[k << 1].w += a[k].flag * a[k << 1].dis();
    a[k << 1 | 1].w += a[k].flag * a[k << 1 | 1].dis();
    a[k << 1].flag += a[k].flag;
    a[k << 1 | 1].flag += a[k].flag;
    a[k].flag = 0;
}

void update(int k, int l, int r, int w)
{
    if(a[k].l==l&&a[k].r==a[k].l)
    {
        a[k].w+=a[k].dis()*w;
        a[k].flag+=w;
        return ;
    }
    if(a[k].flag)
        down(k);
    if(a[k].mid()>=l)
        update(k<<1,l,r,w);
    if(a[k].mid()<r)
        update(k<<1|1,l,r,w);
    a[k].w=a[k<<1].w+a[k<<1|1].w;
}

int query(int k, int l, int r)
{
    if (a[k].l >= l && a[k].r <= r)
        return a[k].w;
    if (a[k].flag)
        down(k);
    int sum = 0;
    if (a[k].mid() >= l)
        sum += query(k << 1, l, r);
    if (a[k].mid() < r)
        sum += query(k << 1 | 1, l, r);
    a[k].w = a[k << 1].w + a[k << 1 | 1].w;
    return sum;
}


int main()
{
    //IN;
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    int T,ii=0;
    cin >> T;
    while (T--)
    {
        ii++;
        cout<<"Case "<<ii<<":"<<endl;
        int n;
        cin>>n;
        build(1,1,n);
        while(1)
        {
            string s;
            int i,j;
            cin>>s;
            if(s=="End")
                break;
            cin>>i>>j;
            if(s=="Sub")
            j=-j;
            if(s=="Query"){
                cout<<query(1,i,j)<<endl;
                continue;
            }
            update(1,i,i,j);
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值