P3373 【模板】线段树 2

线段是是一个很好用的数据结构,特别是在于长长的板子

-----------------

链接:Miku

----------------

先来一份线段树1的代码

#include<iostream>
#include<cstdio>
#include<algorithm>
int n,m;
long long sum[400005], lazy[400005];
int f,x,y;
long long k;
void pushdown(int x, int L, int R){
    if (lazy[x] != 0){
        int mid = (L + R) >> 1;
        lazy[x << 1] += lazy[x];
        lazy[x << 1 | 1] += lazy[x];
        sum[x << 1] += lazy[x] * (mid - L + 1);
        sum[x << 1 | 1] += lazy[x] * (R - mid);
        lazy[x] = 0;
    }
    return;
}
void pushup(int x){
    sum[x] = sum[x << 1] + sum[x << 1 | 1];
    return;
}
void update(int x, int l, int r, int L, int R, long long d){
    if (L <= l && r <= R){
        lazy[x] += d;
        sum[x] += d * (r - l + 1);
        return;
    }
    int mid = (l + r) >> 1;
    pushdown(x, l, r);
    if (L <= mid) update(x << 1, l, mid, L, R, d);
    if (R > mid) update(x << 1 | 1, mid + 1, r, L, R, d);
    pushup(x);
}
long long query(int x, int l, int r, int L, int R){
    if (L <= l && r <= R){
        return sum[x];
    }
    int mid = (l + r) >> 1;
    pushdown(x, l, r);
    long long ans = 0;
    if (L <= mid) ans += query(x << 1, l, mid, L, R);
    if (R > mid) ans += query(x << 1 | 1, mid + 1, r, L, R);
    return ans;
}
int main(){
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;++i){
        scanf("%d",&x);
        update(1, 1, n, i, i, x);
    }
    for(int i=1;i<=m;++i){
        scanf("%d",&f);
        if(f==1){
            scanf("%d%d%lld",&x,&y,&k);
            update(1, 1, n, x, y, k);
        }
        else{
            scanf("%d%d",&x,&y);
            printf("%lld\n", query(1, 1, n, x, y));
        }
    }
    return 0;
}
Ac

---------------

2的难度提升了,因为有两种不同的操作,加和乘。

我们不难发现,应该先乘后加,不然会碰到一个除法。

并且在乘的时候要给两个懒标记一块乘

注意懒标记的初始化和取模

------------

代码时间

(线段树这东西太长了)

#include<iostream>
#include<cstdio>
int n,m;
long long mod = 571373;
long long sum[400005],lazyadd[400005],lazymul[400005];
int x,y,f;
long long k;
void pushdown(int x,int l,int r){
    if(lazymul[x]!=1){
        int mid=(l+r)>>1;
        lazyadd[x<<1]*=lazymul[x];lazyadd[x<<1]%=mod;//运用乘法分配律,把加发标记乘一下 
        lazyadd[x<<1|1]*=lazymul[x];lazyadd[x<<1|1]%=mod;
        lazymul[x<<1]*=lazymul[x];lazymul[x<<1]%=mod;//乘法标记乘起来就行了 
        lazymul[x<<1|1]*=lazymul[x];lazymul[x<<1|1]%=mod;
        sum[x<<1]*=lazymul[x];sum[x<<1]%=mod;//区间和肯定是直接乘的 
        sum[x<<1|1]*=lazymul[x];sum[x<<1|1]%=mod;
        lazymul[x]=1;
    }
    if (lazyadd[x] != 0){//然后处理加法 
        int mid=(l+r)>>1;
        lazyadd[x<<1]+=lazyadd[x];lazyadd[x<<1]%=mod;//和1没大有区别 
        sum[x<<1]+=lazyadd[x]*(mid-l+1);sum[x<<1]%=mod;
        lazyadd[x<<1|1]+=lazyadd[x];lazyadd[x<<1|1]%=mod;
        sum[x<<1|1]+=lazyadd[x]*(r-mid);sum[x<<1|1]%=mod;
        lazyadd[x]=0;
    }
    return ;
}
void pushup(int x){1 
    sum[x]=sum[x<<1]+sum[x<<1|1];
    sum[x]%=mod;//记得mod 
    return ;
}
void update1(int x,int l,int r,int L,int R,long long d){//节约空间牺牲时间的行为,而且要小心大小写的问题、
//这个行为就是把这个点负责的左右区间放在函数里
//如果存在数组里的话
//用空间换时间 
//简单的+ 
    if(L<=l&&r<=R){
        lazyadd[x]+=d;
        lazyadd[x]%=mod;
        sum[x]+=d*(r-l+1);
        sum[x]%=mod;
        return ;
    }
    int mid=(l+r)>>1;
    pushdown(x,l,r);
    if(L<=mid) update1(x<<1,l,mid,L,R,d);
    if(R>mid) update1(x<<1|1,mid+1,r,L,R,d);
    pushup(x);
}
void update2(int x,int l,int r,int L,int R,long long d){
    if(L<=l&&r<=R){
        lazyadd[x]*=d;//乘法分配律告诉我们,两个一起改 
        lazyadd[x]%=mod;
        lazymul[x]*=d;
        lazymul[x]%=mod;
        sum[x]*=d;//显然 
        sum[x]%=mod;
        return ;
    }
    int mid=(l+r)>>1;
    pushdown(x,l,r);
    if(L<=mid) update2(x<<1,l,mid,L,R,d);
    if(R>mid) update2(x<<1|1,mid+1,r,L,R,d);
    pushup(x);
}
long long query(int x,int l,int r,int L,int R){
    if(L<=l&&r<=R){
        return sum[x];
    }
    long long ans=0;
    int mid=(l+r)>>1;
    pushdown(x,l,r);
    if(L<=mid) ans+=query(x<<1,l,mid,L,R),ans%=mod;
    if(R>mid) ans+=query(x<<1|1,mid+1,r,L,R),ans%=mod;
    return ans;
}
int main(){
    scanf("%d%d%lld",&n,&m,&mod);
    for(int i=1;i<=400002;++i)
        lazymul[i]=1;
    for(int i=1;i<=n;++i){
        scanf("%d",&x);
        update1(1,1,n,i,i,x);
    }
    for(int i=1;i<=m;++i){
        scanf("%d",&f);
        if(f==1){
            scanf("%d%d%lld",&x,&y,&k);//嗯,这里的数字有点反 
            update2(1,1,n,x,y,k);
        }
        if(f==2){
            scanf("%d%d%lld",&x,&y,&k);
            update1(1,1,n,x,y,k);
        }
        if(f==3){
            scanf("%d%d",&x,&y);
            printf("%lld\n",query(1,1,n,x,y));
        }
    }
    return 0;
}
Ac

 

posted @ 2020-02-22 18:28  Simex  阅读(119)  评论(0编辑  收藏  举报