线段树记忆(普通版)

学习资料:

https://wmathor.com/index.php/archives/1175/

https://www.cnblogs.com/jason2003/p/9676729.html

https://www.bilibili.com/video/av40780551/?spm_id_from=333.788.b_636f6d6d656e74.11

洗澡去了,改日码一份记忆模板:

 

/*思路:线段树维护区间剩余容量和,查询操作直接输出,
从x层倒入操作则二分查找倒入的液体能到达的层数r,此时x到r-1层剩余容量为0,故区间更新为0,
单点更新r层剩余容量 = x到r层总容量tmp - 导入液体总体积val
(时间长没写线段树,忘了懒惰标记怎么用了,参考下楼里某位dalao的标记。很好用哈哈
*/
#include <bits/stdc++.h>
#define lc rt << 1
#define rc rt << 1 | 1
#define LL long long
using namespace std;
const int AX = 2e5 + 666 ;
LL s[AX<<2] ; // 维护各层剩余容量和
int lazy[AX<<2];
LL v[AX] ;
int n , m ;
LL left_v , tmp ;

/*
s[]是数组
v[]是树
lazy是标记

*/

void pushUp( int rt ){
    s[rt] = s[lc] + s[rc];
    return ;
}

void pushDown( int rt ){
    if( lazy[rt] != -1 ){
        s[lc] = s[rc] = 0 ;
        lazy[lc] = lazy[rc] = 1 ;
        lazy[rt] = -1 ;
    }
    return ;
}

void build( int rt , int l , int r ){
    if( l == r ){
        scanf("%lld",&s[rt]);
        v[l] = s[rt] ;
        return ;
    }
    int mid = ( l + r ) >> 1 ;
    build( lc , l , mid );
    build( rc , mid + 1 , r );
    pushUp(rt);
}

void update( int rt , int l , int r , int L , int R , int op ){
    if( L > r || R < l ) return ;
    if( L <= l && R >= r ){
        if( op ){
            s[rt] = 0 ;
            lazy[rt] = 1 ;
        }else s[rt] = left_v ;
        return ;
    }
    int mid = ( l + r ) >> 1 ;
    pushDown( rt ) ;
    if( l <= mid ) update( lc , l , mid , L , R , op );
    if( R > mid ) update( rc , mid + 1 , r , L , R , op );
    pushUp(rt) ;
}

LL query( int rt , int l , int r , int L , int R ){
    if( L <= l && R >= r ) return s[rt] ;
    int mid = ( l + r ) >> 1 ;
    LL ans = 0 ;
    pushDown( rt );
    if( L <= mid ) ans += query( lc , l , mid , L , R ) ;
    if( R > mid ) ans += query( rc , mid + 1 , r , L , R );
    return ans ;
}

int getR( int l , int r , int x , int val ){ // 查找从x层倒,能满(四声)到哪一层为止
    while( l <= r ){
        int mid = ( l + r ) >> 1 ;
        if( query( 1 , 1 , n , x , mid ) >= val ) r = mid - 1;
        else l = mid + 1 ;
    }
    tmp = query( 1 , 1 , n , x , l );  //查询被倒入液体触及的所有层总容量
    return l ;
}

int main(){
    memset( lazy , -1 , sizeof(lazy) );
    cin >> n >> m ;
    build( 1 , 1 , n ) ;
    int op , x ;
    LL val ;
    while( m-- ){
        scanf("%d",&op);
        if( op == 1 ){
            scanf("%d",&x);
            printf("%lld\n",v[x] - query( 1 , 1 , n , x , x ) );
        }else{
            scanf("%d%lld",&x,&val);
            int r = getR( x , n , x , val ) ;
            left_v = tmp - val ;
            if( left_v <= 0 ){ //正好倒满或倒满地上了
                update( 1 , 1 , n , x , r , 1 );
            }else{
                if( r == 1 ) update( 1 , 1 , n , 1 , 1 , 0 ); //第一层都倒不满
                else{  //x到r-1层被倒满,剩余体积设为0,r层剩余left_v体积 = tmp(x到r层总容量)-val(总共倒入体积)
                    update( 1 , 1 , n , x , r - 1 , 1 ) ;
                    update( 1 , 1 , n , r , r , 0 );
                }
            }
        }
    }
    return 0 ;
}
#include <iostream>
#include <stdio.h>
#define ll long long
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
using namespace std;

const int MAXN = 1e5 + 10;
ll sum[MAXN << 2];
ll add[MAXN << 2];

void push_up(int rt){//向上更新
    sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}

void push_down(int rt, int m){
    if(add[rt]){//若有标记,则将标记向下移动一层
        add[rt << 1] += add[rt];
        add[rt << 1 | 1] += add[rt];
        sum[rt << 1] += (m - (m >> 1)) * add[rt];
        sum[rt << 1 | 1] += (m >> 1) * add[rt];
        add[rt] = 0;//取消本层标记
    }
}

void build(int l, int r, int rt){//建树
    add[rt] = 0;
    if(l == r){
        scanf("%lld", &sum[rt]);
        return;
    }
    int mid = (l + r) >> 1;
    build(lson);
    build(rson);
    push_up(rt);//向上更新
}

void update(int L, int R, ll key, int l, int r, int rt){//区间更新
    if(L <= l && R >= r){
        sum[rt] += (r - l + 1) * key;
        add[rt] += key;
        return;
    }
    push_down(rt, r - l + 1);//向下更新
    int mid = (l + r) >> 1;
    if(L <= mid) update(L, R, key, lson);
    if(R > mid) update(L, R, key, rson);
    push_up(rt);//向上更新
}

ll query(int L, int R, int l, int r, int rt){//区间求和
    if(L <= l && R >= r) return sum[rt];
    push_down(rt, r - l + 1);//向下更新
    int mid = (l + r) >> 1;
    ll ans = 0;
    if(L <= mid) ans += query(L, R, lson);
    if(R > mid) ans += query(L, R, rson);
    return ans;
}

int main(void){
    int n, m;
    scanf("%d%d", &n, &m);
    build(1, n, 1);
    while(m--){
        char str[3];
        int x, y;
        ll z;
        scanf("%s", str);
        if(str[0] == 'C'){
            scanf("%d%d%lld", &x, &y, &z);
            update(x, y, z, 1, n, 1);
        }else{
            scanf("%d%d", &x, &y);
            printf("%lld\n", query(x, y, 1, n, 1));
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值