• 线段树lazy标记1:加乘混合


    题目

    Description

    给定一个正整数序列A,要求支持以下操作 
    1):  + a b c    表示在[a,b]上加上一个常数C。 
    2):  * a b c    在[a,b]上乘上一个常数K。 
    3):  QUERY a b  查询[a,b]的sum。 

    Input

    第一行两个正整数n、m,n表示序列长度,m表示操作数 
    第二行n个正整数,第i表示A[i]的大小 
    接下来的m行,每行有且仅有一种操作,具体和题目描述一致? 
    n,m<=100000 
    其他权值都<=50000 
    小心爆int 

    Output

    对于每个询问操作,输出答案对1000000007取余的结果 

    Sample Input

    10 10
    50 14 20 18 19 11 43 43 26 44
    + 3 6 41
    QUERY 1 2
    + 5 5 14
    QUERY 1 3
    QUERY 4 4
    QUERY 2 6
    * 3 5 31
    * 4 8 20
    * 5 8 28
    QUERY 6 7

    Sample Output

    64
    125
    59
    260
    53200

    思路:

    这是一道很明显的线段树修改题;

    写这题前你必须知道普通的lazy标记怎么写;

    那么这题有什么不一样呢,很显然是要考虑加乘的顺序;

    如(a+b)*c ,a为线段树某一区间和,b为这个区间的 加法lazy标记,c为要乘上的数;

    那么就要 a*c+b*c ,而不是 a*c+b;

    所以我们不仅需要 把a(也就是某一区间和)*c 还要把b(这个区间的加法lazy标记)*c;

    当然除了下传加法标记,也要下传乘法标记;

    所以这一题不一样的是只要在下传乘法标记的时候,把加法标记也乘个c就ok了


    代码:

    #include<bits/stdc++.h>
    typedef long long ll;
    using namespace std;
    inline ll read()
    {
        ll a=0,f=1; char c=getchar();
        while (c<'0'||c>'9') {if (c=='-') f=-1; c=getchar();}
        while (c>='0'&&c<='9') {a=a*10+c-'0'; c=getchar();}
        return a*f;
    }
    const ll mod=1000000007;
    ll n,m,aa[1000006];
    struct sbbb
    {
        ll l,r,v,f,ff=1;
    }a[5000001];
    inline ll R(ll x)//计算x的右节点的编号
    {
        return x*2+1;
    }
    inline ll L(ll x)//计算x的左节点的编号
    {
        return x*2;
    }
    inline void doit(ll p)//区间维护
    {
        a[p].v=(a[L(p)].v+a[R(p)].v)%mod;
    }
    inline void build(ll p,ll l,ll r)//建树
    {
        a[p].l=l;a[p].r=r;
        if(l==r)
        {
            a[p].v=aa[l];
            return;
        }
        ll mid=(l+r)>>1;
        build(L(p),l,mid);
        build(R(p),mid+1,r);
        doit(p);
    }
    inline void push_down(ll p)//标记下传
    {
        ll d=a[p].ff;
        a[L(p)].v=(a[L(p)].v*d)%mod;
        a[R(p)].v=(a[R(p)].v*d)%mod;//每个区间和乘上d
        a[L(p)].ff=(a[L(p)].ff*d)%mod;//标记下传
        a[R(p)].ff=(a[R(p)].ff*d)%mod;//标记下传
        a[L(p)].f=(a[L(p)].f*d)%mod;//综“思路”所述,加法标记也要乘上
        a[R(p)].f=(a[R(p)].f*d)%mod;//综“思路”所述,加法标记也要乘上
        a[p].ff=1;
        if(a[p].f)//加法标记的下传
        {
            ll x=a[p].f;
            a[L(p)].v=((a[L(p)].r-a[L(p)].l+1)*x+a[L(p)].v)%mod;
            a[R(p)].v=((a[R(p)].r-a[R(p)].l+1)*x+a[R(p)].v)%mod;
            a[L(p)].f=(a[L(p)].f+x)%mod;
            a[R(p)].f=(a[R(p)].f+x)%mod;
            a[p].f=0;
        }
    }
    inline void change(ll p,ll l,ll r,ll x)//区间加
    {
        if(l<=a[p].l&&r>=a[p].r)
        {
            a[p].v=((a[p].r-a[p].l+1)*x+a[p].v)%mod;
            a[p].f=(a[p].f+x)%mod;
            return;
        }
        push_down(p);//标记下传
        ll mid=(a[p].l+a[p].r)>>1;
        if(l<=mid)
            change(L(p),l,r,x);
        if(r>mid)
            change(R(p),l,r,x);
        doit(p);
    }
    inline void change1(ll p,ll l,ll r,ll x)//区间乘法
    {
        if(l<=a[p].l&&r>=a[p].r)
        {
            a[p].v=(a[p].v*x)%mod;
            a[p].ff=(a[p].ff*x)%mod;
            a[p].f=(a[p].f*x)%mod;//综“思路”所述,加法标记也要乘上
            return;
        }
        push_down(p);
        ll mid=(a[p].l+a[p].r)>>1;
        if(l<=mid)
            change1(L(p),l,r,x);
        if(r>mid)
            change1(R(p),l,r,x);
        doit(p);
    }
    inline ll findout(ll p,ll l,ll r)//区间统计
    {
        if(l<=a[p].l&&r>=a[p].r)
            return a[p].v;
        push_down(p);
        ll mid=(a[p].l+a[p].r)>>1;
        ll sum=0;
        if(l<=mid)
            sum=(sum+findout(L(p),l,r))%mod;
        if(r>mid)
            sum=(sum+findout(R(p),l,r))%mod;
        return sum;
    }
    int main()
    {
        n=read();m=read();
        for(ll i=1;i<=n;i++)
            aa[i]=read();
        build(1,1,n);//冬眠假期刚刚建树,我还有点糊涂
        for(ll i=1;i<=m;i++)
        {
            char c[5];
            scanf("%s",c);
            if(c[0]=='Q')
            {
                ll x=read(),y=read();
                ll ans=findout(1,x,y);
                printf("%lld
    ",ans);
            }
            else if(c[0]=='+')
            {
                ll x=read(),y=read(),z=read();
                change(1,x,y,z);
            }
            else
            {
                ll x=read(),y=read(),z=read();
                change1(1,x,y,z);
            }
        }
        return 0;//别忘了return 0;
    }
  • 相关阅读:
    手动配置linux(centos)的IP地址
    linux(centos)上配置nginx、mysql、phpfpm开机启动
    visual studio 2022 下载地址
    自己动手开发编译器(五)miniSharp语言的词法分析器
    自己动手开发编译器(一)编译器的模块化工程
    自己动手开发编译器(二)正则语言和正则表达式
    趣味问题:你能用Reflection.Emit生成这段代码吗?
    自己动手开发编译器(零)序言
    自己动手开发编译器特别篇——用词法分析器解决背诵圣经问题
    自己动手开发编译器(三)有穷自动机
  • 原文地址:https://www.cnblogs.com/wzx-RS-STHN/p/13230308.html
Copyright © 2020-2023  润新知