[线段树模板题] 线段树2

好久不见. 提交于 2020-04-04 10:00:09

洛谷原题

第二次做 线段树2 了,之前研究了两节晚自习自以为完全理解了,但是今天写还是有一点小差错,值得反思。

稍微总结下线段树2的要点。

<1> 乘法标记影响加法标记,更新 乘法标记 时应将对应的 加法标记 乘上 乘法标记乘的值(key)

<2> 标记下传中 乘法标记 应优先于 加法标记,由于我们在更新 乘法标记 时更新了 加法标记,所以如果乘法不优先的话,先乘再加的操作会出现误差

<3> 加或乘的操作中每一层算完儿子节点后都应更新当前节点信息,查询则不需要,更新操作只在 子区间 和 其子区间(down) 中进行,所以父区间都应由儿子更新

<4> memset有毒,害我找了40+分钟错误,我一开始用memset将乘法标记设为1,但样例怎么样都过不了,还是看了之前自己的题解,改成 建树过程中将标记设为1 才AC的

下面是我的代码(以前代码是真的丑)

#include <bits/stdc++.h>
using namespace std; 
long long a[100005],he[800005],lazy[800005],hard[800005];
int ll,rr,n,m;
long long ans,P;
void biu(int t,int l,int r)
{
    hard[t]=1;//memset有毒,别用 
    if(l==r)
    {
        he[t]=a[l]%P;
        return;
    }
    int mid=l+r>>1;
    biu(2*t,l,mid);
    biu(2*t+1,mid+1,r);
    he[t]=(he[2*t]+he[2*t+1])%P;
    return;
}
void down(int t,int l,int r)//标记下传 
{
    if(lazy[t]==0&&hard[t]==1) return;
    //乘法优先:乘标记影响加标记
    lazy[2*t]=(lazy[2*t]*hard[t])%P;
    lazy[2*t+1]=(lazy[2*t+1]*hard[t])%P;
     
    hard[2*t]=(hard[2*t]*hard[t])%P;
    hard[2*t+1]=(hard[2*t+1]*hard[t])%P;
    
    lazy[2*t]=(lazy[2*t]+lazy[t])%P;
    lazy[2*t+1]=(lazy[2*t+1]+lazy[t])%P;
    
    int mid=l+r>>1;
    he[2*t]=(((he[2*t]*hard[t])%P)+((lazy[t]*(mid-l+1))%P))%P;
    he[2*t+1]=(((he[2*t+1]*hard[t])%P)+((lazy[t]*(r-mid))%P))%P;
    hard[t]=1;
    lazy[t]=0;
}
void ask(int t,int l,int r)
{
    if(ll<=l&&r<=rr)
    {
        ans=(ans+he[t])%P;
        return;
    }
    down(t,l,r);
    int mid=l+r>>1;
    if(ll<=mid) ask(2*t,l,mid);
    if(rr> mid) ask(2*t+1,mid+1,r);
    //he[t]=(he[2*t]+he[2*t+1])%P; 没更新 ,不用加 
    return;
}
void jia(int t,int l,int r,long long key)
{
    if(ll<=l&&r<=rr)
    {
        he[t]=(he[t]+((r-l+1)*key%P))%P;
        lazy[t]=(lazy[t]+key)%P;
        return;
    }
    int mid=l+r>>1;
    down(t,l,r);
    if(ll<=mid) jia(2*t,l,mid,key);
    if(rr> mid) jia(2*t+1,mid+1,r,key);
    he[t]=(he[2*t]+he[2*t+1])%P;
    return;
}
void cheng(int t,int l,int r,long long key)
{
    if(ll<=l&&r<=rr)
    {
        he[t]=(he[t]*key)%P;
        hard[t]=(hard[t]*key)%P;
        lazy[t]=(lazy[t]*key)%P;
        //printf("Edge %d sum=%lld\n",t,he[t]);
        return;
    }
    int mid=l+r>>1;
    down(t,l,r);
    if(ll<=mid) cheng(2*t,l,mid,key);
    if(rr> mid) cheng(2*t+1,mid+1,r,key);
    he[t]=(he[2*t]+he[2*t+1])%P;
    return;
}
int main()  
{  
    scanf("%d%d%lld",&n,&m,&P);
    int x;long long k;
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    //memset(hard,1,sizeof(hard));
    biu(1,1,n);
    for(int i=1;i<=m;i++)
    {
        scanf("%d",&x);
        if(x==1){scanf("%d%d%lld",&ll,&rr,&k);cheng(1,1,n,k);}
        if(x==2){scanf("%d%d%lld",&ll,&rr,&k);  jia(1,1,n,k);}
        if(x==3){scanf("%d%d",&ll,&rr);ask(1,1,n);printf("%lld\n",ans);ans=0;}
    }
    return 0;  
}  

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!