洛谷p3373(自己附上的解释)

#include <cstdio>
#define lson rt<<1,l,mid//这个#define就很有用处了 
#define rson rt<<1|1,mid+1,r
#define Maxn 500000
#define ll long long
#define lld lld
using namespace std;
ll seg[Maxn];//记录区间i的和 
ll marka[Maxn];//记录 i区间要加的数 
ll markm[Maxn];//记录 i区间乘的数 
ll mod;//记录模数 
void pushup(ll rt)
{
    seg[rt]=(seg[rt<<1]+seg[rt<<1|1])%mod;//已知两个子区间各自的和 计算其父亲区间的和 
}
void pushdown(ll rt,ll x)//下传 
{
    marka[rt<<1]=(marka[rt]+marka[rt<<1]*markm[rt])%mod;//lazy1 下放标记值 
    marka[rt<<1|1]=(marka[rt]+marka[rt<<1|1]*markm[rt])%mod;
        
    markm[rt<<1]=(markm[rt]*markm[rt<<1])%mod;//lazy2 下放标记值 
    markm[rt<<1|1]=(markm[rt]*markm[rt<<1|1])%mod;
        //rt的和在进行下放前已经被更新了 
    seg[rt<<1]=(marka[rt]*(x-x/2)+seg[rt<<1]*markm[rt])%mod;//更新 已被下放的区间的和 
    seg[rt<<1|1]=(marka[rt]*(x/2)+seg[rt<<1|1]*markm[rt])%mod;
        
    marka[rt]=0;//清空 
    markm[rt]=1;//清空 
}
void build(ll rt,ll l,ll r)//建树 
{
    markm[rt]=1;//初始化乘值   
    if (l==r)
        scanf("%lld",&seg[rt]);
    else
    {
        ll mid=(l+r)/2;
        build(lson); 
        build(rson);
        pushup(rt);//到这一步时 rt两个子区间的和已经出来了 求rt的和 
    }
}
ll query(ll rt,ll l,ll r,ll L,ll R)
{
    if (l>=L && r<=R)//若符合的话 rt这个区间就是要求的区间的子区间 
        return seg[rt];//直接返回 这个区间的和 这也是为什么要预先处理被标记的区间的和  
    else
    {
        pushdown(rt,r-l+1);//下放 
        ll mid=(r+l)/2,ans=0;
        if (mid>=L)
            ans=(ans+query(lson,L,R))%mod;//左区间和要求的区间有交集 
        if (mid<R)
            ans=(ans+query(rson,L,R))%mod;//右区间和要求的区间有交集 
        return ans%mod;
    }
}
void update_a(ll rt,ll l,ll r,ll L,ll R,ll x)
{
    if (l>=L && r<=R)
        seg[rt]=(seg[rt]+(r-l+1)*x)%mod,marka[rt]=(x+marka[rt])%mod;
    else
    {
        pushdown(rt,r-l+1);
        ll mid=(r+l)/2;
        if (mid>=L)
            update_a(lson,L,R,x);
        if (mid<R)
            update_a(rson,L,R,x);
        pushup(rt);
    }
}
void update_m(ll rt,ll l,ll r,ll L,ll R,ll x)
{
    if (l>=L && r<=R)
        seg[rt]=(seg[rt]*x)%mod,marka[rt]=(x*marka[rt])%mod,markm[rt]=(x*markm[rt])%mod;
    else
    {
        pushdown(rt,r-l+1);
        ll mid=(r+l)/2;
        if (mid>=L)
            update_m(lson,L,R,x);
        if (mid<R)
            update_m(rson,L,R,x);
        pushup(rt);
    }
}
main()
{
    ll n,m,x,y,z,c;
    scanf("%lld%lld%lld",&n,&m,&mod);
    
        build(1,1,n);
        while(m--)
        {
            scanf("%lld",&c);
            if(c==1)//乘值 
            {
                scanf("%lld%lld%lld",&x,&y,&z);
                update_m(1,1,n,x,y,z);//找到被包含的区间 更新和值并标记 
            }
            else
            if(c==2)//加值 
            {
                scanf("%lld%lld%lld",&x,&y,&z); 
                update_a(1,1,n,x,y,z);//同上
            }
            else
            if (c==3)
            {
                scanf("%lld%lld",&x,&y);
                printf("%lld\n",query(1,1,n,x,y));//输出 
            }
        }
    

}


程序转自http://www.yhzq-blog.cc/%e7%ba%bf%e6%ae%b5%e6%a0%91%e9%ab%98%e9%98%b6%e6%80%bb%e7%bb%93/

不过 略加修改

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值