线段树。。。。。(加法和进阶加乘法混合)

 

 基础的线段树就是去构建一个树,这个树符合二分,每个节点p的子节点是2*p,2*p+1。。。

因为1-2-4-8-。。。。1节点2,3。2个,然后下面节点就是4567,4个正好首节点大小个。。多巧。。。

然后维护这个树。当然可以实时更新整个树,但是时间复杂度大,所以我们使用懒标记。

如果改变的区域包含了一个大块,我们直接只更新大块的值并打上标记 (下面一样的递归办法)  ,并在用到时把堆积的懒标记下传

在询问时,判断范围,如果在范围内,直接就是最大范围的值,如果不在,就取1节点的两个二分区间,比较一下大小,如果两边都有就是都有,往下去一直找。。比如1-16的大区间。我给个5-11改变;往下走,发现5-8这个直接就是一个,9,10是一个,11是一个。到此推完

#include<bits/stdc++.h>
using namespace std;
const int ma=100010;
long long a[ma+2];
struct tree{
    int l,r;
    long long pr,add;
}t[4*ma+2];

void bulid(int p,int l,int r){
    t[p].l=l;t[p].r=r;
    if(l==r){
        t[p].pr=a[l];
        return;
    }
    int mid=l+r>>1;
    bulid(p*2,l,mid);
    bulid(p*2+1,mid+1,r);
    t[p].pr=t[p*2].pr+t[p*2+1].pr;
} 
void sp(int p){
    if(t[p].add){
        t[p*2].pr+=t[p].add*(t[p*2].r-t[p*2].l+1);
        t[p*2+1].pr+=t[p].add*(t[p*2+1].r-t[p*2+1].l+1);
        t[p*2].add+=t[p].add;
        t[p*2+1].add+=t[p].add;
        t[p].add=0;
    }
}
void change(int p,int x,int y,long long z){
    if(x<=t[p].l && y>=t[p].r){
        t[p].pr+=(long long)z*(t[p].r-t[p].l+1);
        t[p].add+=z;
        return;
    }
    sp(p);
    int mid=t[p].l+t[p].r>>1;
    if(x<=mid) change(p*2,x,y,z);
    if(y>mid) change(p*2+1,x,y,z);
    t[p].pr=t[p*2].pr+t[p*2+1].pr;   
}
long long ask(int p,int x,int y){
    if(x<=t[p].l && y>=t[p].r) return t[p].pr;
    sp(p);
    int mid=t[p].l+t[p].r>>1;
    long long ans=0;
    if(x<=mid) ans+=ask(p*2,x,y);
    if(y>mid) ans+=ask(p*2+1,x,y);
    return ans;
}
int main(){
	ios_base::sync_with_stdio(false);
	cin.tie(nullptr);
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    cin>>a[i];
    bulid(1,1,n);
    for(int i=1;i<=m;i++)
    {
        int q,x,y;
        long long z;
        cin>>q;
        if(q==1){
            cin>>x>>y>>z;
            change(1,x,y,z);
        }
        else {
            cin>>x>>y;
            cout<<ask(1,x,y)<<endl;
        }
    }
    return 0;
}

有非常多的细节,调试好久。。。注意该开long long 的地方 

//题解 P3372 【【模板】线段树 1】 - 洛谷专栏(留给我自己看的

 

 搞清楚乘法加法关系即可,每一个加法tag都实时在乘法时被乘。。。。参考数学原理想想就懂了

#include<bits/stdc++.h>
using namespace std;
const int ma=100010;
long long a[ma+2];
int h;
struct tree{
    int l,r;
    long long pr,add,mul;
}t[4*ma+2];

void bulid(int p,int l,int r){
    t[p].l=l;t[p].r=r;
    t[p].mul = 1; 
    t[p].add = 0;
    if(l==r){
        t[p].pr=a[l]%h;
        return;
    }
    int mid=l+r>>1;
    bulid(p*2,l,mid);
    bulid(p*2+1,mid+1,r);
    t[p].pr=(t[p*2].pr+t[p*2+1].pr)%h;
} 
void sp(int p){
	if (t[p].mul == 1 && t[p].add == 0) return;
    else{
        t[p*2].pr=(t[p*2].pr*t[p].mul+t[p].add*(t[p*2].r-t[p*2].l+1))%h;
        t[p*2+1].pr=(t[p*2+1].pr*t[p].mul+t[p].add*(t[p*2+1].r-t[p*2+1].l+1))%h;
        t[p*2].add=(t[p*2].add*t[p].mul+t[p].add)%h;
        t[p*2+1].add=(t[p*2+1].add*t[p].mul+t[p].add)%h;
        t[p*2].mul=(t[p*2].mul*t[p].mul)%h;
        t[p*2+1].mul=(t[p*2+1].mul*t[p].mul)%h;        
        t[p].add=0;t[p].mul=1;
    }
}
void change1(int p,int x,int y,long long z){
    if(x<=t[p].l && y>=t[p].r){
        t[p].pr = (t[p].pr * z) % h;  
        t[p].mul = (t[p].mul * z) % h; 
        t[p].add = (t[p].add * z) % h;  
        return;
    }
    sp(p);
    int mid=t[p].l+t[p].r>>1;
    if(x<=mid) change1(p*2,x,y,z);
    if(y>mid) change1(p*2+1,x,y,z);
    t[p].pr=(t[p*2].pr+t[p*2+1].pr)%h;   
}
void change2(int p,int x,int y,long long z){
    if(x<=t[p].l && y>=t[p].r){
        t[p].pr = (t[p].pr + z * (t[p].r - t[p].l + 1)) % h;  
        t[p].add = (t[p].add + z) % h;
        return;
    }
    sp(p);
    int mid=t[p].l+t[p].r>>1;
    if(x<=mid) change2(p*2,x,y,z);
    if(y>mid) change2(p*2+1,x,y,z);
    t[p].pr = (t[p*2].pr + t[p*2+1].pr) % h;;   
}
long long ask(int p, int x, int y) {
    if (x <= t[p].l && y >= t[p].r) return t[p].pr % h; 
    sp(p);
    int mid = t[p].l + t[p].r >> 1;
    long long ans = 0;
    if (x <= mid) ans = (ans + ask(p*2, x, y)) % h; 
    if (y > mid) ans = (ans + ask(p*2+1, x, y)) % h; 
    return ans % h; 
}
int main(){
	ios_base::sync_with_stdio(false);
	cin.tie(nullptr);
    int n,m;
    cin>>n>>m>>h;
    for(int i=1;i<=n;i++){
    cin>>a[i];
	a[i]%=h;	
	}
    
    bulid(1,1,n);
    for(int i=1;i<=m;i++)
    {
        int q,x,y;
        long long z;
        cin>>q;
        if(q==1){
            cin>>x>>y>>z;
            z=z%h;
            change1(1,x,y,z);
        }
        else if(q==2){
            cin>>x>>y>>z;
            z=z%h;
            change2(1,x,y,z);        	
		}
        else {
            cin>>x>>y;
            cout<<ask(1,x,y)<<endl;
        }
    }
    return 0;
}

### 线段树实现区间加法乘法操作 #### 结构定义 为了支持区间的加法乘法操作,线段树中的每个节点除了存储左右边界`l`, `r`以及当前区间的总`sum`外,还需要额外维护两个懒惰标签`add`用于记录待处理的加法操作,`mul`用于记录待处理的乘法操作。 ```cpp struct Node { int l, r; long long sum; long long mul; // 乘法懒标记 long long add; // 加法懒标记 } tree[N << 2]; ``` #### 延迟更新函数 当访问某个节点时,如果该节点存在未处理的操作,则先将其应用于子节点上,并清除自身的延迟标志位。对于乘法来说,在传递给子节点之前要确保已有的增值也被相应扩大;而对于加法则只需简单地累即可[^2]。 ```cpp void pushdown(int p) { if (tree[p].mul != 1 || tree[p].add != 0) { int mid = (tree[p].l + tree[p].r) >> 1; // 更新左孩子 tree[p<<1].sum *= tree[p].mul; tree[p<<1].sum += (mid-tree[p].l+1)*tree[p].add; tree[p<<1].mul *= tree[p].mul; tree[p<<1].add *= tree[p].mul; tree[p<<1].add += tree[p].add; // 更新右孩子 tree[(p<<1)|1].sum *= tree[p].mul; tree[(p<<1)|1].sum += (tree[p].r-mid)*tree[p].add; tree[(p<<1)|1].mul *= tree[p].mul; tree[(p<<1)|1].add *= tree[p].mul; tree[(p<<1)|1].add += tree[p].add; // 清除父节点的懒标记 tree[p].mul = 1; tree[p].add = 0; } } ``` #### 查询与修改 无论是查询还是修改都需要遵循自顶向下遍历的原则,即每次到达一个新的内部结点都要调用一次`pushdown()`方法以保证数据的一致性。具体到本题中: - **单点/区间求**:通过递归方式计算目标区间的累积值。 - **区间增**:将增量附至对应区间的根节点并设置其`add`属性。 - **区间相乘**:同样作用于指定区间的根节点但这次是调整它的`mul`属性[^4]。 ```cpp // 区间加法 void update_add(int L, int R, long long val, int p=1){ if(L<=tree[p].l && tree[p].r<=R){ tree[p].sum+=(tree[p].r-tree[p].l+1)*val; tree[p].add+=val; return ; } pushdown(p); int m=(tree[p].l+tree[p].r)>>1; if(L<=m)update_add(L,R,val,p*2); if(R>m)update_add(L,R,val,p*2+1); tree[p].sum=tree[p*2].sum+tree[p*2+1].sum; } // 区间乘法 void update_mul(int L,int R,long long val,int p=1){ if(L<=tree[p].l&&tree[p].r<=R){ tree[p].sum*=val; tree[p].mul*=val; tree[p].add*=val; return ; } pushdown(p); int m=(tree[p].l+tree[p].r)>>1; if(L<=m)update_mul(L,R,val,p*2); if(R>m)update_mul(L,R,val,p*2+1); tree[p].sum=tree[p*2].sum+tree[p*2+1].sum; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值