AHOI 2009 行星序列 BZOJ 1798 COGS 1272 线段树模板题:加、乘标记

裸的不能再裸的线段树,拿这个练练手,防手生。
需要两个标记,加和乘。列个式子看一看就能明白:当新标记是加时,直接加到原加标记中,新标记是乘时,既要乘到原乘标记中,还要乘到加标记中。这样在pushdown时先下放乘标记再下放加标记即可。

需要注意的是不要忘了在修改时是要pushdown两遍的,其实第二次说是maintain更合适,因为只需要再计算一次sum[p] = sum[lc]+sum[rc],直接写一句话也可以。

#include <cstdio>
#include <algorithm>
#include <cstring>
#define M 100005
#define lc p<<1
#define rc p<<1|1
#define mid ((l+r)>>1)
typedef long long L;
using namespace std;

int x, a, b;
L n, m, P, c;
L sum[M<<2], mul[M<<2], plu[M<<2];

void build(int p, int l, int r){
    mul[p] = 1;
    if(l == r){
        scanf("%lld", sum+p);
        sum[p] %= P;
        return ;
    }
    build(lc, l, mid);
    build(rc, mid+1, r);
    sum[p] = sum[lc] + sum[rc];
    sum[p] %= P;
}

L cal(int p, int len){
    return ((sum[p]*mul[p]%P)+plu[p]*len)%P;
}

void pushdown(int p, int l, int r){
    mul[lc] = mul[lc]*mul[p]%P;
    mul[rc] = mul[rc]*mul[p]%P;
    plu[lc] = plu[lc]*mul[p]%P;
    plu[rc] = plu[rc]*mul[p]%P;
    plu[lc] = plu[lc]+plu[p]%P;
    plu[rc] = plu[rc]+plu[p]%P;
    sum[p] = ((cal(lc, mid-l+1)+cal(rc, r-mid)))%P;
    mul[p] = 1; plu[p] = 0;
}

void mult(int p, int l, int r){
    if(a <= l && r <= b){
        mul[p] = mul[p]*c%P;
        plu[p] = plu[p]*c%P;
        return ;
    }
    pushdown(p, l, r);
    if(a <= mid) mult(lc, l, mid);
    if(b > mid) mult(rc, mid+1, r);
    sum[p] = ((cal(lc, mid-l+1)+cal(rc, r-mid)))%P; //NO.2 “pushdown”
}

void plus(int p, int l, int r){
    if(a <= l && r <= b){
        plu[p] = (plu[p]+c)%P;
        return ;
    }
    pushdown(p, l, r);
    if(a <= mid) plus(lc, l, mid);
    if(b > mid) plus(rc, mid+1, r);
    sum[p] = ((cal(lc, mid-l+1)+cal(rc, r-mid)))%P;
}

L query(int p, int l, int r){
    if(a <= l && r <= b){
        return cal(p, r-l+1);
    }
    pushdown(p, l, r);
    L res = 0;
    if(a <= mid) res = (res+query(lc, l, mid))%P;
    if(b > mid) res = (res+query(rc, mid+1, r))%P;
    return res;
}

int main()
{
    scanf("%lld %lld", &n, &P);
    build(1, 1, n);
    scanf("%lld", &m);
    while(m--){
        scanf("%d %d %d", &x, &a, &b);
        if(x == 1){
            scanf("%lld", &c);
            mult(1, 1, n);
        }
        if(x == 2){
            scanf("%lld", &c);
            plus(1, 1, n);
        }
        if(x == 3){
            printf("%lld\n", query(1, 1, n));
        }
    }   
    return 0;
} 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值