线段树

来源:互联网 发布:手机我的世界js编辑器 编辑:程序博客网 时间:2024/05/29 03:23

贾比奇的博客

带lazytag的线段树模板

/*第一行包含两个整数N、M,分别表示该数列数字的个数和操作的总个数。第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。接下来M行每行包含3或4个整数,表示一个操作,具体如下:操作1: 格式:1 x y k 含义:将区间[x,y]内每个数加上k操作2: 格式:2 x y 含义:输出区间[x,y]内每个数的和*/#include<iostream>#include<cstdio>using namespace std;long long m,n,x,y,k,z,b,sum[1000001],g[1000001],ans;inline long long read(){    long long x=0;char y;    do    {        y=getchar();    }    while(y<'0'||y>'9');    do    {        x=10*x+y-'0';        y=getchar();    }    while(y<='9'&&y>='0');    return x;}void build(int rt,int l,int r){    if(l==r)     {        sum[rt]=read();         return;    }    long long m=(l+r)/2;    build(rt*2,l,m);    build(rt*2+1,m+1,r);    sum[rt]=sum[2*rt]+sum[2*rt+1];}void pd(int rt,int l,int r){    int m=r-l+1;    g[rt*2]+=g[rt];    g[rt*2+1]+=g[rt];//这个地方一定要有“+”,卡了我一上午    sum[rt*2+1]+=(m/2)*g[rt];    sum[rt*2]+=(m-(m/2))*g[rt];    g[rt]=0;}void update(long long rt,long long l,long long r){    if(l>=x&&r<=y)    {        g[rt]+=z;        sum[rt]+=(r-l+1)*z;        return;    }//如果当前节点的区间在查询区间里,直接把lazytag放在这就好了    if(g[rt]) pd(rt,l,r);//如果不在,就把lazytag向下移    long long m=(l+r)/2;    if(m>=x) update(rt*2,l,m);    if(y>m) update(rt*2+1,m+1,r);    sum[rt]=sum[rt*2]+sum[rt*2+1];//更新sum}long long q(long long rt,long long l,long long r){    int pc;    if(l>=x&&r<=y)     return sum[rt];    long long m=0,mm=(l+r)/2;    if(y>mm) m+=q(2*rt+1,mm+1,r);    if(x<=mm)m+=q(2*rt,l,mm);    return m;}int main(){    cin>>n>>m;    build(1,1,n);//建树    for(int i=1;i<=m;i++)    {        b=read();        if(b==1)        {            x=read();y=read();z=read();            update(1,1,n);//更新        }        else        {            x=read();y=read();            printf("%lld\n",q(1,1,n));//询问        }    }}

带乘法的线段树模板

只是加了一个乘法数组
每次更新时,乘法可以改变子节点加数和乘数

/*输入格式:第一行包含三个整数N、M、P,分别表示该数列数字的个数、操作的总个数和模数。第二行包含N个用空格分隔的整数,其中第i个数字表示数列第i项的初始值。接下来M行每行包含34个整数,表示一个操作,具体如下:操作1: 格式:1 x y k 含义:将区间[x,y]内每个数乘上k操作2: 格式:2 x y k 含义:将区间[x,y]内每个数加上k操作3: 格式:3 x y 含义:输出区间[x,y]内每个数的和对P取模所得的结果*/#include<iostream>#include<cstdio>using namespace std;long long sum[1000001],ad[1000001],mu[10000001],n,m,k,p,pc,x,y,z;void build(int rt,int l,int r){    if(l==r) scanf("%d",&sum[rt]);    else    {        long long mm=(l+r)/2;        build(rt*2,l,mm);        build(rt*2+1,mm+1,r);        sum[rt]=sum[rt*2]+sum[rt*2+1];    }}void pd(int rt,int l,int r){    long long rs,ls,mm;    ls=2*rt;    rs=2*rt+1;    mm=(l+r)/2;    mu[ls]*=mu[rt];    mu[rs]*=mu[rt];    ad[ls]*=mu[rt];    ad[rs]*=mu[rt];    ad[ls]+=ad[rt];    ad[rs]+=ad[rt];    mu[ls]%=p;    mu[rs]%=p;    ad[ls]%=p;    ad[rs]%=p;    sum[ls]=(sum[ls]*mu[rt]+ad[rt]*(mm-l+1))%p;    sum[rs]=(sum[rs]*mu[rt]+ad[rt]*(r-mm))%p;    ad[rt]=0;    mu[rt]=1;}void multi(int rt,int l,int r){    if(l>=x&&r<=y) //乘数可以改变加数和乘数    {        mu[rt]*=z;        ad[rt]*=z;              sum[rt]*=z;        mu[rt]%=p;        ad[rt]%=p;        sum[rt]%p;        return;    }    pd(rt,l,r);    long long mm=(l+r)/2;    if(x<=mm) multi(rt*2,l,mm);    if(y>mm) multi(rt*2+1,mm+1,r);    sum[rt]=(sum[rt*2]+sum[rt*2+1])%p;}void add(int rt,int l,int r){    if(l>=x&&r<=y)    {        ad[rt]+=z;        sum[rt]+=(r-l+1)*z;        ad[rt]%=p;        sum[rt]%=p;        return;    }    pd(rt,l,r);    long long mm=(l+r)/2;    if(x<=mm) add(rt*2,l,mm);    if(y>mm) add(rt*2+1,mm+1,r);    sum[rt]=(sum[rt*2]+sum[rt*2+1])%p;}long long q(int rt ,int l,int r){    long long m=0;int mm=(l+r)/2;    if(l>=x&&r<=y)    {        return sum[rt]%p;    }    pd(rt,l,r);    if(x<=mm) m=(m+q(rt*2,l,mm))%p;    if(y>mm) m=(m+q(rt*2+1,mm+1,r))%p;    return m%p;}int main(){    cin>>n>>m>>p;    for(int i=1;i<=2*n+3;i++)    mu[i]=1;    build(1,1,n);    for(int i=1;i<=m;i++)    {        scanf("%d",&pc);        if(pc==1)         {            scanf("%d%d%d",&x,&y,&z);            multi(1,1,n);        }        if(pc==2)        {            scanf("%d%d%d",&x,&y,&z);            add(1,1,n);        }        if(pc==3)        {            scanf("%d%d",&x,&y);            printf("%lld\n",q(1,1,n));        }    }}