[模板]线段树

codevs4927

1、注意代码是左闭右闭的线段树,mid在左区间内
2、打权值线段树时注意线段树右端点大于最大值

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define L(w) w << 1
#define R(w) w << 1|1
#define INF 1061109567
typedef long long LL;
using namespace std;
const int MAXN = 200000 + 50;
int L[MAXN << 2],R[MAXN << 2],maxn[MAXN << 2],minx[MAXN << 2],M1[MAXN << 2],M2[MAXN << 2];
LL sum[MAXN << 2];
int A[MAXN];
void update(int w){
    sum[w] = sum[L(w)] + sum[R(w)];
    minx[w] = min(minx[L(w)],minx[R(w)]);
    maxn[w] = max(maxn[L(w)],maxn[R(w)]);
}
void spread1(int w){
    if(M1[w] == -1)return;
    int m = M1[w];
    M1[w] = -1;
    minx[L(w)] = maxn[L(w)] = m;
    minx[R(w)] = maxn[R(w)] = m;
    sum[L(w)] = m*(R[L(w)] - L[L(w)] + 1);
    sum[R(w)] = m*(R[R(w)] - L[R(w)] + 1);
    M2[L(w)] = M2[R(w)] = 0;
    M1[L(w)] = M1[R(w)] = m;
}
void spread(int w){
    if(M1[w] != -1){
        spread1(w);
    }
    int m = M2[w];
    if(!m)return;
    M2[w] = 0;
    spread1(L(w));
    spread1(R(w));
    maxn[L(w)] += m;
    maxn[R(w)] += m;
    minx[L(w)] += m;
    minx[R(w)] += m;
    sum[L(w)] += m*(R[L(w)] - L[L(w)] + 1);
    sum[R(w)] += m*(R[R(w)] - L[R(w)] + 1);
    M2[L(w)] += m;
    M2[R(w)] += m;
}
void build(int w,int l,int r){
    L[w] = l;R[w] = r;
    if(l == r){
        sum[w] = minx[w] = maxn[w] = A[l];
        return;
    }
    int mid = l + r >> 1;
    build(L(w),l,mid);
    build(R(w),mid + 1,r);
    update(w);
}
void cover(int w,int l,int r,int v){
    if(L[w] == l && R[w] == r){
        M2[w] = 0;
        M1[w] = v;
        minx[w] = maxn[w] = v;
        sum[w] = v*(R[w] - L[w] + 1);
        return;
    }
    spread(w);//重置前,先下放标记 
    int mid = L[w] + R[w] >> 1;
    if(r <= mid)cover(L(w),l,r,v);
    else if(l > mid)cover(R(w),l,r,v);
    else cover(L(w),l,mid,v),cover(R(w),mid + 1,r,v);
    update(w);
}
void add(int w,int l,int r,int v){
    if(L[w] == l && R[w] == r){
        spread1(w);
        M2[w] += v;
        minx[w] += v;
        maxn[w] += v;
        sum[w] += v*(R[w] - L[w] + 1);
        return;
    }
    spread(w);
    int mid = L[w] + R[w] >> 1;
    if(r <= mid)add(L(w),l,r,v);
    else if(l > mid)add(R(w),l,r,v);
    else add(L(w),l,mid,v),add(R(w),mid + 1,r,v);
    update(w);
}
LL ask_sum(int w,int l,int r){
    if(L[w] == l && R[w] == r){
        return sum[w];
    }
    spread(w);
    LL ans = 0;
    int mid = L[w] + R[w] >> 1;
    if(r <= mid)ans = ask_sum(L(w),l,r);
    else if(l > mid)ans = ask_sum(R(w),l,r);
    else ans = ask_sum(L(w),l,mid) + ask_sum(R(w),mid + 1,r);
    return ans;
}
int ask_max(int w,int l,int r){
    if(L[w] == l && R[w] == r){
        return maxn[w];
    }
    spread(w);
    int mid = L[w] + R[w] >> 1;
    int ans = -INF;
    if(r <= mid)ans = ask_max(L(w),l,r);
    else if(l > mid)ans = ask_max(R(w),l,r);
    else ans = max(ask_max(L(w),l,mid),ask_max(R(w),mid + 1,r));
    return ans;
}
int ask_min(int w,int l,int r){
    if(L[w] == l && R[w] == r){
        return minx[w];
    }
    spread(w);
    int mid = L[w] + R[w] >> 1;
    int ans = INF;
    if(r <= mid)ans = ask_min(L(w),l,r);
    else if(l > mid)ans = ask_min(R(w),l,r);
    else ans = min(ask_min(L(w),l,mid),ask_min(R(w),mid + 1,r));
    return ans;
}
int n,m,a,b,c;
string s;
int main(){
    memset(M1,-1,sizeof(M1));
    scanf("%d%d",&n,&m);
    for(int i = 1;i <= n;i ++){
        scanf("%d",&A[i]);
    }
    build(1,1,n);
    for(int i = 1;i <= m;i ++){
        cin >> s;
        if(s == "add"){
            scanf("%d%d%d",&a,&b,&c);
            add(1,a,b,c);
        }
        else if(s == "set"){
            scanf("%d%d%d",&a,&b,&c);
            cover(1,a,b,c);
        }
        else if(s == "sum"){
            scanf("%d%d",&a,&b);
            if(a > b)swap(a,b);
            printf("%lld\n",ask_sum(1,a,b));
        }
        else if(s == "max"){
            scanf("%d%d",&a,&b);
            printf("%d\n",ask_max(1,a,b));
        }
        else if(s == "min"){
            scanf("%d%d",&a,&b);
            printf("%d\n",ask_min(1,a,b));
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值