线段树模版

https://www.luogu.org/problem/show?pid=3373
lazy尽量在函数一开头就下传!!!!
线段树,感觉是很难的,打个模到简单,但是实际的题目出来,光会模版基本上是没用的;
但是连模版都不会那就
这一道题目是线段树的区间加乘,区间求和,线段树的概念我就不细讲了,反正类似与二叉树;
这里的lazy个人是比较赞同的,它的特点是当一个节点num要打上lazy标记前,先算好,所以这就是为什么这个lazy的clean函数这么长,因为它要把两个子节点的lazy都算好;
当然咯,因为是加乘混用,所以在clean时需要有顺序,貌似不是所有的运算都可以成功的一起存储,所以打线段树前,一定要想好,想好;
这种东西以来100+的代码,不是好调试的;
不说什么了,自己看吧

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
struct cs{
    LL ll,rr,vv,chen,jia;
}T[850000];
LL a[200000];
LL n,m,p,x,y,z;
void maketree(LL x,LL y,LL num){
    T[num].ll=x; T[num].rr=y; T[num].chen=1; T[num].jia=0;
    if(x==y){
        T[num].vv=a[x]; return;
    }
    LL mid=(x+y)>>1,k=num<<1;
    maketree(x,mid,k);
    maketree(mid+1,y,k+1);
    T[num].vv=T[k].vv+T[k+1].vv;
}
void clean(LL num){
    if(T[num].chen==1&&T[num].jia==0)return;
    LL k=num<<1;
    T[k].vv=(T[k].vv*T[num].chen+(T[k].rr-T[k].ll+1)*T[num].jia)%p;
    T[k+1].vv=(T[k+1].vv*T[num].chen+(T[k+1].rr-T[k+1].ll+1)*T[num].jia)%p;
    T[k].chen=(T[k].chen*T[num].chen)%p;
    T[k].jia=(T[k].jia*T[num].chen+T[num].jia)%p;
    T[k+1].chen=(T[k+1].chen*T[num].chen)%p;
    T[k+1].jia=(T[k+1].jia*T[num].chen+T[num].jia)%p;
    T[num].chen=1; T[num].jia=0;
}
void mul(LL num){
    clean(num);
    if(x<=T[num].ll&&T[num].rr<=y){
        T[num].vv=(T[num].vv*z)%p;
        T[num].chen=z;
        return;
    }
    LL k=num<<1;
    if(x<=T[k].rr)mul(k);
    if(y>=T[k+1].ll)mul(k+1);
    T[num].vv=(T[k].vv+T[k+1].vv)%p;
}
void pluss(LL num){
    clean(num);
    if(x<=T[num].ll&&T[num].rr<=y){
        T[num].vv=(T[num].vv+(T[num].rr-T[num].ll+1)*z)%p;
        T[num].jia=z;
        return;
    } 
    LL k=num<<1;
    if(x<=T[k].rr)pluss(k);
    if(y>=T[k+1].ll)pluss(k+1);
    T[num].vv=(T[k].vv+T[k+1].vv)%p;
}
LL out(int num){
    clean(num);
    if(x<=T[num].ll&&T[num].rr<=y)return T[num].vv%p;
    LL k=num<<1;
    LL ans=0;
    if(x<=T[k].rr)ans=out(k);
    if(y>=T[k+1].ll)ans+=out(k+1);
    return ans%p;
}
int main()
{
    scanf("%lld%lld%lld",&n,&m,&p);
    for(int i=1;i<=n;i++)scanf("%lld",&a[i]),a[i]%=p;
    maketree(1,n,1);
    while(m--){
        scanf("%lld",&x);
        if(x==1){
            scanf("%lld%lld%lld",&x,&y,&z);z%=p;mul(1);
        }else
        if(x==2){
            scanf("%lld%lld%lld",&x,&y,&z);z%=p;pluss(1);
        }else{
            scanf("%lld%lld",&x,&y);
            printf("%lld\n",out(1));
        }        
    }
}

现在的题目好难啊,线段树一单和其它的东西混合起来,基本功没打扎实的同学完蛋了;

posted @ 2017-02-13 19:17  largecube233  阅读(122)  评论(0编辑  收藏  举报