线段树(加法和乘法的模板)

来源:互联网 发布:网络打假举报电话是 编辑:程序博客网 时间:2024/05/16 20:46

所有的操作都没什么很大的变化
我们处理加法和乘法的时候,默认乘法操作优先于加法操作

在计算一个结点的值的时候:
val=val*mul+len*add
mul:乘法标记
len:区间长度
add:加法标记

在下传标记的时候,
儿子的值我们也是按照上面那样算

注意,标记代表的是对当前结点已经进行过的操作,但是还没有对儿子进行过该操作

整个算法中,最难的就是儿子的标记维护

child.mul=child.mul*fa.mul

child.add=child.add*fa.mul+fa.add

  • 儿子节点的add标记分别先乘上父亲节点的乘标记再加上父亲节点的加标记
    (因为父亲节点的加标记已经乘上了一些乘标记了,不需要再乘一次)
  • 儿子节点的乘标记直接乘上父亲节点的乘标记。

tip

开ll
随时%
mul标记的初始值是1,add标记初始值是0
在修改值的时候,add的维护需要累加,mul的维护需要累乘

//这里写代码片#include<cstdio>#include<cstring>#include<iostream>#define ll long longusing namespace std;const int N=100010;struct node{    int x,y;    ll v,mul,ad;};node t[N<<2];int n,m;ll a[N],p;void update(int bh){    t[bh].v=t[bh<<1].v+t[(bh<<1)+1].v;    t[bh].v%=p;}void push(int bh)        //默认乘操作在加操作之前 {    int lc=bh<<1;    int rc=(bh<<1)+1;    if (t[bh].mul!=1||t[bh].ad!=0)    {        t[lc].v=( (t[lc].v*t[bh].mul)%p + ((t[lc].y-t[lc].x+1)*t[bh].ad)%p )%p;        t[lc].mul=(t[bh].mul*t[lc].mul)%p;        t[lc].ad=(t[lc].ad*t[bh].mul)%p+t[bh].ad; t[lc].ad%=p;        t[rc].v=( (t[rc].v*t[bh].mul)%p + ((t[rc].y-t[rc].x+1)*t[bh].ad)%p )%p;        t[rc].mul=(t[bh].mul*t[rc].mul)%p;        t[rc].ad=(t[rc].ad*t[bh].mul)%p+t[bh].ad; t[rc].ad%=p;        t[bh].ad=0;        t[bh].mul=1;    }    return;}void build(int bh,int l,int r){    t[bh].x=l;    t[bh].y=r;    t[bh].mul=1; t[bh].ad=0;    if (l==r)    {        t[bh].v=a[l]%p;        return;    }    int mid=(l+r)>>1;    build(bh<<1,l,mid);    build(bh<<1|1,mid+1,r);    update(bh);}void add(int bh,int l,int r,ll val){    push(bh);    if (t[bh].x>=l&&t[bh].y<=r)    {        t[bh].ad+=val; t[bh].ad%=p;        t[bh].v=( t[bh].v*t[bh].mul%p + ((t[bh].y-t[bh].x+1)*t[bh].ad)%p )%p;        return;    }    int mid=(t[bh].x+t[bh].y)>>1;    if (l<=mid) add(bh<<1,l,r,val);    if (r>mid) add((bh<<1)+1,l,r,val);    update(bh);}void multi(int bh,int l,int r,ll val){    push(bh);    if (t[bh].x>=l&&t[bh].y<=r)    {        t[bh].mul*=val; t[bh].mul%=p;        t[bh].v=( t[bh].v*t[bh].mul%p + ((t[bh].y-t[bh].x+1)*t[bh].ad)%p )%p;        return;    }    int mid=(t[bh].x+t[bh].y)>>1;    if (l<=mid) multi(bh<<1,l,r,val);    if (r>mid) multi((bh<<1)+1,l,r,val);    update(bh);}int ask(int bh,int l,int r){    push(bh);    if (t[bh].x>=l&&t[bh].y<=r)        return t[bh].v%p;    ll ans=0;    int mid=(t[bh].x+t[bh].y)>>1;    if (l<=mid) ans+=ask(bh<<1,l,r);    if (r>mid) ans+=ask((bh<<1)+1,l,r);    return ans%p;}int main(){    scanf("%d%d%lld",&n,&m,&p);    for (int i=1;i<=n;i++) scanf("%lld",&a[i]);    build(1,1,n);    int opt,x,y;    ll z;    for (int i=1;i<=m;i++)    {        scanf("%d%d%d",&opt,&x,&y);        if (opt==3){            printf("%lld\n",ask(1,x,y)%p);        }        else if (opt==2)        {            scanf("%lld",&z);            add(1,x,y,z%p);        }        else         {            scanf("%lld",&z);            multi(1,x,y,z%p);        }    }    return 0;}