30pts求助
查看原帖
30pts求助
419173
hiro653楼主2023/2/18 16:04
//洛谷 P3372,线段树,区间修改 + 区间查询
#include<bits/stdc++.h>
using namespace std;
#define ll long long

const int N = 1e5 + 10;
ll a[N];        //记录数列的元素,从a[1]开始
ll tree[N<<2];  //tree[i]:第i个结点的值,表示一个线段区间的值,例如最值、区间和
ll tag_add[N<<2];   //tag_add[i]:第i个结点的lazy-tag,统一记录这个区间的修改
ll tag_mul[N<<2];
ll ls(ll p){ return p<<1;  }           //定位左儿子:p*2
ll rs(ll p){ return p<<1|1;}           //定位右儿子:p*2 + 1
int MOD=0;
void push_up(ll p){                    //从下往上传递区间值
    tree[p] = (tree[ls(p)] + tree[rs(p)])%MOD; 
     //本题是区间和。如果求最小值,改为:tree[p] = min(tree[ls(p)], tree[rs(p)]);
}
void build(ll p,ll pl,ll pr){    //建树。p是结点编号,它指向区间[pl, pr]
    tag_add[p] = 0;                         //lazy-tag标记
    if(pl==pr){tree[p]=a[pl]%MOD; return;}  //最底层的叶子,赋值    
    ll mid = (pl+pr) >> 1;              //分治:折半
    build(ls(p),pl,mid);                //左儿子
    build(rs(p),mid+1,pr);              //右儿子
    push_up(p);                         //从下往上传递区间值
} 
void add(ll p,ll pl,ll pr,ll d){     //给结点p打tag标记,并更新tree
    tag_add[p] = (tag_add[p]+d%MOD)%MOD;                        //打上tag标记
    tree[p] = (tree[p]+(d%MOD)*(pr-pl+1))%MOD;             //计算新的tree
}
void mul(ll p,ll pl,ll pr,ll d){     //给结点p打tag标记,并更新tree
    if(!tag_mul[p]) tag_mul[p]=d%MOD; 
    else tag_mul[p] =(tag_mul[p]* d)%MOD;           //打上tag标记
    tag_add[p]=(tag_add[p]*(d%MOD))%MOD;
    tree[p] =(tree[p]%MOD*d%MOD)%MOD;             //计算新的tree
}
void push_down(ll p,ll pl,ll pr){       //不能覆盖时,把tag传给子树
    
    if(tag_add[p]||tag_mul[p]){                         //有tag标记,这是以前做区间修改时留下的
        ll mid = (pl+pr)>>1; 
        //先乘后加
        if(tag_mul[p]){
            mul(ls(p),pl,mid,tag_mul[p]);    //把tag标记传给左子树
            mul(rs(p),mid+1,pr,tag_mul[p]);  //把tag标记传给右子树
            tag_mul[p]=0;
        }
        add(ls(p),pl,mid,tag_add[p]);    //把tag标记传给左子树
        add(rs(p),mid+1,pr,tag_add[p]);  //把tag标记传给右子树
        tag_add[p]=0;                       //p自己的tag被传走了,归0
    }
}

void update_add(ll L,ll R,ll p,ll pl,ll pr,ll d){ //区间修改:把[L, R]内每个元素加上d
    if(L<=pl && pr<=R){       //完全覆盖,直接返回这个结点,它的子树不用再深入了    
        add(p, pl, pr,d);  //给结点p打tag标记,下一次区间修改到p时会用到
        return;                    
    }
    //如果不能覆盖,表示该区间的一致性遭到破坏,先push_down把tag传给子树
    push_down(p,pl,pr);                 
    ll mid=(pl+pr)>>1;
    if(L<=mid) update_add(L,R,ls(p),pl,mid,d);    //递归左子树
    if(R>mid)  update_add(L,R,rs(p),mid+1,pr,d);  //递归右子树
     //至此左右子树都已经更新,push_up更新parent
    push_up(p);                              
}

void update_mul(ll L,ll R,ll p,ll pl,ll pr,ll d){ //区间修改:把[L, R]内每个元素加上d
    if(L<=pl && pr<=R){       //完全覆盖,直接返回这个结点,它的子树不用再深入了    
        mul(p, pl, pr,d);  //给结点p打tag标记,下一次区间修改到p时会用到
        return;                    
    }
    //如果不能覆盖,表示该区间的一致性遭到破坏,先push_down把tag传给子树
    push_down(p,pl,pr);                 
    ll mid=(pl+pr)>>1;
    if(L<=mid) update_mul(L,R,ls(p),pl,mid,d);    //递归左子树
    if(R>mid)  update_mul(L,R,rs(p),mid+1,pr,d);  //递归右子树
     //至此左右子树都已经更新,push_up更新parent
    push_up(p);                              
}

ll query(ll L,ll R,ll p,ll pl,ll pr){
  //查询区间[L,R];p是当前结点(线段)的编号,[pl,pr]是结点p表示的线段区间
    if(pl>=L && R >= pr) return tree[p]%MOD;       //完全覆盖,直接返回
    push_down(p,pl,pr);                        //不能覆盖,递归子树
    ll res=0;
    ll mid = (pl+pr)>>1;
    if(L<=mid) res=(res+query(L,R,ls(p),pl,mid))%MOD;   //左子结点有重叠
    if(R>mid)  res=(res+query(L,R,rs(p),mid+1,pr))%MOD; //右子结点有重叠
    return res%MOD;
}

void mdf(ll p,ll pl,ll pr){

    tag_add[p]=0;
    tag_mul[p]=0;
    if(pl==pr) {
        tree[p]=0;
        return;
    }
    ll mid=pl+pr>>1;
    mdf(ls(p),pl,mid);
    mdf(rs(p),mid+1,pr);
    push_up(p);
}
//对乘0的处理
void change(ll L,ll R,ll p,ll pl,ll pr){

     if(pl>=L && R >= pr) { //完全覆盖,直接返回

        mdf(p,pl,pr);
        return;
     }      
    ll mid = (pl+pr)>>1;
    if(L<=mid) change(L,R,ls(p),pl,mid);   //左子结点有重叠
    if(R>mid)  change(L,R,rs(p),mid+1,pr); //右子结点有重叠
    push_up(p);
}

int main(){
    ll n, m;  scanf("%lld%lld%lld",&n,&m,&MOD);
    for(ll i=1;i<=n;i++)  scanf("%lld",&a[i]);
    build(1,1,n);                              //建树
    while(m--){
        ll q,L,R,d;     scanf("%lld",&q);
        if (q==1){                          //区间修改:把[L,R]的每个元素加上d
            scanf("%lld%lld%lld",&L,&R,&d);
            if(d==0) change(L,R,1,1,n);
            else update_mul(L,R,1,1,n,d); 
        }else if(q==2){

            scanf("%lld%lld%lld",&L,&R,&d);
            update_add(L,R,1,1,n,d); 
        }
        else {                                 //区间询问:[L,R]的区间和
            scanf("%lld%lld",&L,&R);
            printf("%lld\n",query(L,R,1,1,n));   
        }       
    }
    return 0;
}

2023/2/18 16:04
加载中...