Luogu P3373 【模板】线段树 2

这还是比较简单的线段树模板题吧

更多的是给一个思路,当维护多种需要Lazy Tag的操作时顺序问题

这里的话加法和查询都可以参照区间加法模板题Sol

主要是当标记下传的时候,要注意先处理乘法在处理加法

Why?

因为显然乘法的优先级比加法高,如果先加后乘就会爆掉

例如:10+6*233

如果先算加法那么就是16*233,那么显然是错的

而且就算是这样的情况如(10+100)*233

也可以把乘法标记先传给加法化成10*233+100*233(乘法分配律的原理,小学数学)

然后规定好顺序就可以直接套线段树的板子了

CODE

#include<cstdio>
using namespace std;
typedef long long LL;
const LL N=100005;
struct segtree
{
    LL sum,add,mul;
}tree[N<<2];
LL n,m,p,a[N],opt,x,y,z;
inline char tc(void)
{
    static char fl[100000],*A=fl,*B=fl;
    return A==B&&(B=(A=fl)+fread(fl,1,100000,stdin),A==B)?EOF:*A++;
}
inline void read(LL &x)
{
    x=0; char ch=tc();
    while (ch<'0'||ch>'9') ch=tc();
    while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=tc();
}
inline void write(LL x)
{
    if (x/10) write(x/10);
    putchar(x%10+'0');
}
inline void up(LL root)
{
    tree[root].sum=(tree[root<<1].sum+tree[root<<1|1].sum)%p;
}
inline void down(LL root,LL l,LL r)
{
    if (tree[root].add||tree[root].mul!=1)
    {
        tree[root<<1].mul=(tree[root<<1].mul*tree[root].mul)%p;
        tree[root<<1|1].mul=(tree[root<<1|1].mul*tree[root].mul)%p;
        tree[root<<1].add=(tree[root<<1].add*tree[root].mul)%p;
        tree[root<<1|1].add=(tree[root<<1|1].add*tree[root].mul)%p;
        tree[root<<1].sum=(tree[root<<1].sum*tree[root].mul)%p;
        tree[root<<1|1].sum=(tree[root<<1|1].sum*tree[root].mul)%p;
        tree[root].mul=1;
        tree[root<<1].add=(tree[root<<1].add+tree[root].add)%p;
        tree[root<<1|1].add=(tree[root<<1|1].add+tree[root].add)%p;
        tree[root<<1].sum=(tree[root<<1].sum+tree[root].add*l)%p;
        tree[root<<1|1].sum=(tree[root<<1|1].sum+tree[root].add*r)%p;
        tree[root].add=0;
    }
}
inline void build(LL root,LL l,LL r)
{
    tree[root].mul=1;
    if (l==r)
    {
        tree[root].sum=a[l];
        return;
    }
    LL mid=l+r>>1;
    build(root<<1,l,mid); build(root<<1|1,mid+1,r);
    up(root);
}
inline void modify_mul(LL root,LL l,LL r,LL beg,LL end,LL k)
{
    if (l>=beg&&r<=end)
    {
        tree[root].mul=(tree[root].mul*k)%p;
        tree[root].add=(tree[root].add*k)%p;
        tree[root].sum=(tree[root].sum*k)%p;
        return;
    }
    LL mid=l+r>>1;
    down(root,mid-l+1,r-mid);
    if (beg<=mid) modify_mul(root<<1,l,mid,beg,end,k);
    if (mid<end) modify_mul(root<<1|1,mid+1,r,beg,end,k);
    up(root);
}
inline void modify_add(LL root,LL l,LL r,LL beg,LL end,LL k)
{
    if (l>=beg&&r<=end)
    {
        tree[root].add=(tree[root].add+k)%p;
        tree[root].sum=(tree[root].sum+k*(r-l+1))%p;
        return;
    }
    LL mid=l+r>>1;
    down(root,mid-l+1,r-mid);
    if (beg<=mid) modify_add(root<<1,l,mid,beg,end,k);
    if (mid<end) modify_add(root<<1|1,mid+1,r,beg,end,k);
    up(root);
}
inline LL query(LL root,LL l,LL r,LL beg,LL end)
{
    if (l>=beg&&r<=end) return tree[root].sum;
    LL mid=l+r>>1,res=0;
    down(root,mid-l+1,r-mid);
    if (beg<=mid) res=(res+query(root<<1,l,mid,beg,end))%p;
    if (mid<end) res=(res+query(root<<1|1,mid+1,r,beg,end))%p;
    up(root);
    return res;
}
int main()
{
    //freopen("CODE.in","r",stdin); freopen("CODE.out","w",stdout);
    register LL i;
    read(n); read(m); read(p);
    for (i=1;i<=n;++i)
    read(a[i]);
    build(1,1,n);
    while (m--)
    {
        read(opt); read(x); read(y);
        if (opt==1) read(z),modify_mul(1,1,n,x,y,z%p);
        if (opt==2) read(z),modify_add(1,1,n,x,y,z%p);
        if (opt==3) write(query(1,1,n,x,y)),putchar('\n');
    }
    return 0;
}

此题和这道题基本类似完全一样,双倍经验即可(但我打了两次,权当熟练,毕竟线段树真的很基础也重要

posted @ 2018-05-08 13:34  空気力学の詩  阅读(162)  评论(0编辑  收藏  举报