线段树2

Posted popo-black-cat

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了线段树2相关的知识,希望对你有一定的参考价值。

  本以为自己对线段树已经掌握得很好,做完这道题,感觉自己又收货了很多。

  首先我们要定义运算的优先顺序,因为对于两个lazy标记,我们不知道是应该先乘还是应该先加,所以我们就规定:先乘再加,就这么定了!

  可是别高兴太早呀!我们虽然规定了顺序,但是这不一定是真正的顺序,比如 ( a + b ) * c 和 a * c + b 是不一样的,所以呐,我们略加观察——嘿!原来在原有基础上乘一个数,和先乘这个数,与加上加法lazy乘这个 数的和是一样的!那么,我们就可以这样:如果加一个数,就直接加在加法lazy上;如果是乘一个数,那么我们除了在乘法lazy里乘上这个数以外,也在加法lazy里乘这个数,这样先乘后加就一定不会有错啦!当然前提是乘法标记一开始都是1!

  还有一件事,就是lazy标记的下传。比如儿子节点是 ac + b ,父亲节点乘法标记和加法标记是 x , y 。那这个值应该是  ( ac + b ) * x + y,展开后就是 acx + bx + y,那我们发现,这就是在儿子节点的初始权值和的基础上,乘法标记是 c * x ,加法标记是 bx + y。所以我们可以乘法直接乘,加法先乘上父亲节点的乘法标记,再加上父亲节点的加法标记,就是下传后的标记了。

  那话不多说啦(已经说很多了),代码如下:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
#define maxn 2000010
#define int long long 
struct node
{
    int sum,add,mul;
} e[maxn];
int l[maxn],r[maxn];
int n,m,p;
int add(int a,int b)
{
    return (a+b)%p;
}
int mul(int a,int b)
{
    return (a*b)%p;
}
void push_down(int now)
{
    int ls=now*2;
    int rs=now*2+1;
    int mid=(l[now]+r[now])/2;
    e[ls].sum=mul(e[ls].sum,e[now].mul);
    e[ls].sum=add(e[ls].sum,e[now].add*(r[ls]-l[ls]+1));
    e[rs].sum=mul(e[rs].sum,e[now].mul);
    e[rs].sum=add(e[rs].sum,e[now].add*(r[rs]-l[rs]+1));
    
    e[ls].mul=mul(e[ls].mul,e[now].mul);
    e[rs].mul=mul(e[rs].mul,e[now].mul);
    
    e[ls].add=mul(e[ls].add,e[now].mul);
    e[ls].add=add(e[ls].add,e[now].add);
    e[rs].add=mul(e[rs].add,e[now].mul);
    e[rs].add=add(e[rs].add,e[now].add);
    
    e[now].mul=1;
    e[now].add=0;
}
void build(int L,int R,int now)
{
    e[now].mul=1;
    e[now].add=0;
    l[now]=L;
    r[now]=R;
    if(L==R) 
    {
        scanf("%lld",&e[now].sum);
        e[now].sum%=p;
        return;    
    }
    int mid=(L+R)/2;
    build(L,mid,now*2);
    build(mid+1,R,now*2+1);
    e[now].sum=add(e[now*2].sum,e[now*2+1].sum);
    return ;
}
void update_add(int now,int L,int R,int k)
{
    if(L<=l[now]&&r[now]<=R)
    {
        e[now].add=add(e[now].add,k);
        e[now].sum=add(e[now].sum,k*(r[now]-l[now]+1));
        return ;
    }
    push_down(now);
    int mid=(l[now]+r[now])/2;
    if(mid>=L) update_add(now*2,L,R,k);
    if(mid<R) update_add(now*2+1,L,R,k);
    e[now].sum=add(e[now*2].sum,e[now*2+1].sum); 
}
void update_mul(int now,int L,int R,int k)
{
    if(L<=l[now]&&r[now]<=R)
    {
        e[now].mul=mul(e[now].mul,k);
        e[now].add=mul(e[now].add,k);
        e[now].sum=mul(e[now].sum,k);
        return ;
    }
    push_down(now);
    int mid=(l[now]+r[now])/2;
    if(mid>=L) update_mul(now*2,L,R,k);
    if(mid<R) update_mul(now*2+1,L,R,k);
    e[now].sum=add(e[now*2].sum,e[now*2+1].sum);
} 
int query(int now,int L,int R)
{
    if(L<=l[now]&&r[now]<=R)
        return e[now].sum;
    push_down(now);
    int mid=(l[now]+r[now])/2;
    int ans=0;
    if(L<=mid) ans=query(now*2,L,R)%p;
    if(mid<R) ans=add(ans,query(now*2+1,L,R));
    return ans; 
}
main()
{
    scanf("%lld%lld%lld",&n,&m,&p);
    build(1,n,1);    
    for(int i=1;i<=m;i++)
    {
        int op,x,y,k;
        scanf("%lld%lld%lld",&op,&x,&y);
        if(op==1)
        {
            scanf("%lld",&k);
            update_mul(1,x,y,k);
        }
        else if(op==2)
        {
            scanf("%lld",&k);
            update_add(1,x,y,k); 
        }
        else
            printf("%lld
",query(1,x,y));
    }
    return 0;
}

  啊啊啊,还有一道序列操作没 d 出来……

  所以——

 

 

  睡觉去了!

  >\<

以上是关于线段树2的主要内容,如果未能解决你的问题,请参考以下文章

线段树

高级数据结构线段树

数据结构线段树笔记2

线段树2

luogu3373线段树2..支持区间加值和乘值的线段树

数据结构 ---[实现 线段树(SegmentTree) ]