Luogu_3373 (双 tag 线段树模板)

来源:互联网 发布:淘宝加热棒 编辑:程序博客网 时间:2024/06/09 19:32

题目

https://www.luogu.org/problemnew/show/3373

就是维护一个序列,有区间加或乘的操作,区间查询。

分析

  • 线段树弄两种 tag,乘和加,推一下tag操作时的公式即可。
  • 具体可以看程序中的注释。

程序

#include <cstdio>typedef long long ll;ll n,m,Ha,k,k1,k2,k3;struct segment_tree{    #define X t[x]    #define Lx (x<<1)    #define Rx (Lx+1)    #define L t[Lx]    #define R t[Rx]    #define Mid ((X.l+X.r)>>1)    #define sz(x) (t[x].r-t[x].l+1)    struct node{ll s,l,r,T1,T2;} t[2000000];        //[l,r]:  区间*t1+t2    void mer(ll x){        X.s=(L.s*L.T1+sz(Lx)*L.T2)%Ha;        X.s=(X.s+R.s*R.T1+sz(Rx)*R.T2)%Ha;    }    void bui(ll x,ll l,ll r){        X=(node){0,l,r,1,0};        if (l==r) {scanf("%d",&X.s); return;}        bui(Lx,l,Mid);        bui(Rx,Mid+1,r);        mer(x);    }    void add_tag(ll x,ll t1,ll t2){         //(x*T1+T2)*t1+t2=x*(T1*t1)+T2*t1+t2        X.T1=(X.T1*t1)%Ha;        X.T2=(X.T2*t1+t2)%Ha;    }    void down_tag(ll x){            //sigma(i*T1+T2)=sigma(i)*T1+sz*T2        add_tag(Lx,X.T1,X.T2);        add_tag(Rx,X.T1,X.T2);        ll sz=X.r-X.l+1;        X.s=(X.s*X.T1+sz*X.T2)%Ha;        X.T1=1,X.T2=0;    }    ll que(ll x,ll l,ll r){     //询问 [l,r]         down_tag(x);        if (l<=X.l && X.r<=r) return X.s;        ll ret=0;        if (l<=Mid) ret=(ret+que(Lx,l,r))%Ha;        if (r>Mid) ret=(ret+que(Rx,l,r))%Ha;        return ret;    }    void upd(ll x,ll l,ll r,ll t1,ll t2){   //[l,r]*t1+t2        if (l<=X.l && X.r<=r){add_tag(x,t1,t2); return;}        down_tag(x);        if (l<=Mid) upd(Lx,l,r,t1,t2);        if (r>Mid) upd(Rx,l,r,t1,t2);        mer(x);    }} T;int main(){    scanf("%d%d%d",&n,&m,&Ha);    T.bui(1,1,n);    while (m--){        scanf("%d",&k);        if (k==1){            scanf("%d%d%d",&k1,&k2,&k3);            T.upd(1,k1,k2,k3,0);            continue;        }        if (k==2){            scanf("%d%d%d",&k1,&k2,&k3);            T.upd(1,k1,k2,1,k3);            continue;        }        scanf("%d%d",&k1,&k2);        printf("%d\n",T.que(1,k1,k2));    }}