题解:P3373 【模板】线段树 2

· · 题解

本题解使用 动态开点线段树

方法

暴力时间复杂度 O(n^2),肯定挂,考虑线段树。

复习线段树

线段树,将每一个区间分成个两个长度为 \frac{len}{2} 的区间,下图是一棵长度 n4 的线段树,每个点维护一个值 val

建树

对于每个线段 x,先建自己的子树,到最后一层时初始化 val,接下来更新上方的 val

修改

考虑对每个区间修改,发现时间复杂度 O(n\log{n}),比暴力还差。

考虑当这个修改覆盖这一整个区间时,使用懒标记 tag 记录,当下次修改或查询时将懒标记下传给左右子数,别忘了更新上方的 val

查询

几乎与修改一样,这个查询覆盖这一整个区间时,直接返回 val,当下次修改或查询时将懒标记下传给左右子数。

本题题解

考虑将 tag 分成两个,加法标记 add 和乘法标记 mul。其余同上。

注意事项

在下放 add 标记时受 mul 标记影响,更新 val 时先乘后加。

在乘法时也要更新 $add$ 标记,相当于 $add$ 也变成了多倍。 ### 代码 ```cpp #include<bits/stdc++.h> #define int long long using namespace std; const int N=400010; int n,q,m,a[N],rt,tc; int ls[N],rs[N],val[N],add[N],mul[N]; void pushup(int x){ // 更新val,上传 val[x]=(val[ls[x]]+val[rs[x]])%m; } void pushdown(int x,int l,int r){ // 更新 tag,下放(l,r 为区间左右端点) int mid=(l+r)>>1; val[ls[x]]=(val[ls[x]]*mul[x]+add[x]*(mid-l+1))%m; // 先乘后加 val[rs[x]]=(val[rs[x]]*mul[x]+add[x]*(r-mid))%m; mul[ls[x]]=(mul[ls[x]]*mul[x])%m; mul[rs[x]]=(mul[rs[x]]*mul[x])%m; add[ls[x]]=(add[ls[x]]*mul[x]+add[x])%m; // add 更新受 mul 影响 add[rs[x]]=(add[rs[x]]*mul[x]+add[x])%m; mul[x]=1; add[x]=0; } void build(int &x,int l,int r){ // 建树(l,r 为区间左右端点) x=++tc; add[x]=0;mul[x]=1; // 乘法初始化为 1 if(l==r){ val[x]=a[l]; return; } int mid=(l+r)>>1; build(ls[x],l,mid); build(rs[x],mid+1,r); pushup(x); } void update1(int x,int l,int r,int ql,int qr,int v){ // 乘法更新(l,r 为区间左右端点,ql,qr 为查询左右端点) if(ql<=l&&r<=qr){ // 整个区间在更新范围内,打 tag val[x]=(val[x]*v)%m; mul[x]=(mul[x]*v)%m; add[x]=(add[x]*v)%m; // add 标记也会被乘法更新 return; } pushdown(x,l,r); int mid=(l+r)>>1; if(ql<=mid) update1(ls[x],l,mid,ql,qr,v); // 若左子区间在更新范围内,更新 if(mid<qr) update1(rs[x],mid+1,r,ql,qr,v); // 同上 pushup(x); } void update2(int x,int l,int r,int ql,int qr,int v){ // 加法更新(l,r 为区间左右端点,ql,qr 为查询左右端点) if(ql<=l&&r<=qr){ val[x]=(val[x]+v*(r-l+1))%m; add[x]=(add[x]+v)%m; return; } pushdown(x,l,r); int mid=(l+r)>>1; if(ql<=mid) update2(ls[x],l,mid,ql,qr,v); if(mid<qr) update2(rs[x],mid+1,r,ql,qr,v); pushup(x); } int query(int x,int l,int r,int ql,int qr){ // 查询(l,r 为区间左右端点,ql,qr 为查询左右端点) if(ql<=l&&r<=qr) return val[x]; pushdown(x,l,r); int mid=(l+r)>>1,ans=0; if(ql<=mid) ans=(ans+query(ls[x],l,mid,ql,qr))%m; if(mid<qr) ans=(ans+query(rs[x],mid+1,r,ql,qr))%m; return ans; } signed main(){ ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); cin>>n>>q>>m; for(int i=1;i<=n;i++) cin>>a[i]; build(rt,1,n); while(q--){ int op,x,y,k; cin>>op>>x>>y; if(op==1){ cin>>k; update1(rt,1,n,x,y,k); }else if(op==2){ cin>>k; update2(rt,1,n,x,y,k); }else{ cout<<query(rt,1,n,x,y)<<"\n"; } } return 0; } ```