如果我既有區間乘法又有區間加法,我應該怎麼辦呢?
這時候需要寫兩個標記。假設只寫一個標記。
標記加法:此時對於乘法操作,因為是將 \(t_i+lazy_i\) 乘以 \(x\),這樣子顯然一個懶惰標記做不到。
標記乘法:那我加法咋辦?
那兩個標記怎麼用呢?首先假設加法標記為 \(lazy\),乘法標記為 \(multi\)。如果將這兩個標記分開顯然是不可能的,因為每個點的標記是互相依賴的。而如果一直 \(push\_down\) 需要花費 \(\log n\) 的時間,這與線段樹 \(\log n\) 的標準就不一樣了,別忘了還有線段樹本身的操作。如果僅僅是給 \(now\) 下放,那麼子結點會遇到一樣的問題。一般我們做題用的線段樹數量是根據查詢的種類來的。假設這裡只有區間加法查詢,那麼應該怎麼做呢?
首先考慮 \(t\) 的變化,顯然在加法操作時,應當 \(t_{now}+=x\times (tr-tl+1)\),在乘法操作時,應當 \(t_{now}\times =x\)。其次,考慮 \(push\_down\),對於加法的 \(push\_down\),和普通的加法下放一樣,因為加法下方是不考慮乘法標記的,而乘法標記需要考慮加法標記。如果反過來,加法下放考慮乘法標記,這時候 \(lazy_{now}\) 就可能出現分數,就不太方便了。那麼考慮乘法下放。對於一個區間,如果統統乘以 \(x\),那麼這個區間的和就乘以 \(x\),這是乘法分配律。所以,乘法標記下放的時候應該讓 \(t_{now*2(+1)}\) 和 \(lazy\)_{now*2(+1)} 都乘以 \(multi_{now}\)。那麼如果將一個區間都乘以 \(x\) 再都乘以 \(y\),就相當於乘以 \(xy\),所以 \(mutli_{now*2(+1)}\) 應當乘以 \(multi_{now}\)。然後將 \(multi_now\) 清為 \(1\)。在寫修改和查詢的時候,要先下放乘法標記,再下放加法標記,因為乘法標記包含對加法的考慮,所以先下放加法會出現各種各樣的問題。
以下程式碼參考題目:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+10;
int n,p,m,a[N],t[N*8],lazy[N*8],multi[N*8];
void push_down_add(int now,int tl,int tr){
int mid=(tl+tr)/2;
t[now*2]=(t[now*2]+lazy[now]*(mid-tl+1))%p;
t[now*2+1]=(t[now*2+1]+lazy[now]*(tr-mid))%p;
lazy[now*2]=(lazy[now*2]+lazy[now])%p;
lazy[now*2+1]=(lazy[now*2+1]+lazy[now])%p;
lazy[now]=0;
}
void push_down_multi(int now,int tl,int tr){
int mid=(tl+tr)/2;
t[now*2]=t[now*2]*multi[now]%p;
t[now*2+1]=t[now*2+1]*multi[now]%p;
multi[now*2]=multi[now*2]*multi[now]%p;
multi[now*2+1]=multi[now*2+1]*multi[now]%p;
lazy[now*2]=lazy[now*2]*multi[now]%p;
lazy[now*2+1]=lazy[now*2+1]*multi[now]%p;
multi[now]=1;
}
void build(int now,int tl,int tr){
multi[now]=1;
if(tl==tr){
t[now]=a[tl];
return ;
}
int mid=(tl+tr)/2;
build(now*2,tl,mid);
build(now*2+1,mid+1,tr);
t[now]=(t[now*2]+t[now*2+1])%p;
}
void modify_add(int now,int tl,int tr,int l,int r,int x){
if(tl>=l&&tr<=r){
t[now]=(t[now]+x*(tr-tl+1))%p;
lazy[now]=(lazy[now]+x)%p;
return ;
}
if(tl>r||tr<l){
return ;
}
if(multi[now]!=1){
push_down_multi(now,tl,tr);
}
if(lazy[now]){
push_down_add(now,tl,tr);
}
int mid=(tl+tr)/2;
modify_add(now*2,tl,mid,l,r,x);
modify_add(now*2+1,mid+1,tr,l,r,x);
t[now]=(t[now*2]+t[now*2+1])%p;
}
void modify_multi(int now,int tl,int tr,int l,int r,int x){
if(tl>=l&&tr<=r){
t[now]=t[now]*x%p;
multi[now]=multi[now]*x%p;
lazy[now]=lazy[now]*x%p;
return ;
}
if(tl>r||tr<l){
return ;
}
if(multi[now]!=1){
push_down_multi(now,tl,tr);
}
if(lazy[now]){
push_down_add(now,tl,tr);
}
int mid=(tl+tr)/2;
modify_multi(now*2,tl,mid,l,r,x);
modify_multi(now*2+1,mid+1,tr,l,r,x);
t[now]=(t[now*2]+t[now*2+1])%p;
}
int query(int now,int tl,int tr,int l,int r){
if(tl>=l&&tr<=r){
return t[now];
}
if(tl>r||tr<l){
return 0;
}
if(multi[now]!=1){
push_down_multi(now,tl,tr);
}
if(lazy[now]){
push_down_add(now,tl,tr);
}
int mid=(tl+tr)/2;
return (query(now*2,tl,mid,l,r)+query(now*2+1,mid+1,tr,l,r))%p;
}
signed main(){
//freopen("xx.in","r",stdin);
//freopen("xx.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m>>p;
for(int i=1;i<=n;i++){
cin>>a[i];
}
build(1,1,n);
while(m--){
int opt,t,g,c;
cin>>opt;
if(opt==1){
cin>>t>>g>>c;
modify_multi(1,1,n,t,g,c);
}else if(opt==2){
cin>>t>>g>>c;
modify_add(1,1,n,t,g,c);
}else{
cin>>t>>g;
cout<<query(1,1,n,t,g)<<"\n";
}
}
return 0;
}