[Ahoi2009]Seq 維護序列seq(線段樹既有乘又有加的LAZY操作)
阿新 • • 發佈:2019-02-10
題意:給出一個線段,對這個線段的部分既有一段相乘一個數,又有一段相加上一個數,還有中間會詢問一段區間的和是多少?
題解:如果只有一段相加上一個數,那麼很顯然,很板的LAZY操作,但是現在又有了相乘一個數的操作,還在LAZY操作進行解決
現在區間不僅有加法,還有乘法,區間和的形式應該為:ax+b,表示現在的區間和是原來的區間和先乘以a再加上b。
在程式中我們把mu定義為a,sum定義成su,ad定義為b,下傳標記時節點的更新就是su = mu * su + ad
當我們要修改一個區間時,要保證ax+b的形式,即先乘後加的形式。當將區間乘以一個數k時,原來的區間和為ax+b,乘以k得k(ax+b)=kax+kb,也就是把節點的su和ad都乘上一個k。
區間加一個數更加簡單,原來的區間和為ax+b,加上一個k為ax+b+k,合併b, k得ax+(b+k),也就是把原來的ad加上一個k。
我們設要下傳標記的節點的ad為b,mu為a,因此這個節點的和為ax+b,它的一個兒子的ad為b',mu為a',這個節點的和為a'y+b',為了保持先乘後加的順序,先把該節點的和乘以a得aa'y+ab'然後加上b得aa'y+ab'+b合併一下得(aa')y+(ab'+b)也就是把這個節點的兒子的mu乘以這個節點的mu,然後把這個節點的兒子的ad乘以這個節點的mu再加上這個節點的ad,更新這個節點,清空這個節點的標記,然後標記就下傳完畢了。
附上程式碼:
#include<bits/stdc++.h> using namespace std; #define update tr[t].su=tr[t<<1].su+tr[t<<1|1].su;tr[t].su%=M; typedef long long ll; const int N=1e5+50; struct node{ ll mu,su,ad; }tr[N<<2]; int n,M,a[N],op,x,y,m; ll read(){ ll x=0; char ch; do{ ch=getchar(); }while (ch<'0'||ch>'9'); while (ch>='0' && ch<='9') x=(x<<3)+(x<<1)+(ch^48),ch=getchar(); return x; } void build(int t,int l,int r){ tr[t].mu=1; if (l==r){ tr[t].su=a[l]; return; } int mid=l+r>>1; build(t<<1,l,mid); build(t<<1|1,mid+1,r); update; } void maintain(int t,int k){ tr[t<<1].su=(tr[t<<1].su*tr[t].mu+tr[t].ad*(k+1>>1))%M; tr[t<<1|1].su=(tr[t<<1|1].su*tr[t].mu+tr[t].ad*(k>>1))%M; tr[t<<1].mu=tr[t<<1].mu*tr[t].mu%M; tr[t<<1|1].mu=tr[t<<1|1].mu*tr[t].mu%M; tr[t<<1].ad=(tr[t<<1].ad*tr[t].mu+tr[t].ad)%M; tr[t<<1|1].ad=(tr[t<<1|1].ad*tr[t].mu+tr[t].ad)%M; tr[t].mu=1;tr[t].ad=0; } void cheng(int t,int l,int r,ll val){ if (x<=l && r<=y){ tr[t].mu=tr[t].mu*val%M; tr[t].ad=tr[t].ad*val%M; tr[t].su=tr[t].su*val%M; return; } maintain(t,r-l+1); int mid=l+r>>1; if(x>mid){ cheng(t<<1|1,mid+1,r,val); }else if(y<=mid){ cheng(t<<1,l,mid,val); }else{ cheng(t<<1,l,mid,val); cheng(t<<1|1,mid+1,r,val); } update; } void jia(int t,int l,int r,ll val){ if (x<=l && r<=y){ tr[t].ad+=val; tr[t].ad%=M; tr[t].su=(tr[t].su+(r-l+1)*val)%M; return; } maintain(t,r-l+1); int mid=l+r>>1; if(x>mid){ jia(t<<1|1,mid+1,r,val); }else if(y<=mid){ jia(t<<1,l,mid,val); }else{ jia(t<<1,l,mid,val); jia(t<<1|1,mid+1,r,val); } update; } ll query(int t,int l,int r){ if (x<=l && r<=y){ return tr[t].su; } maintain(t,r-l+1); int mid=l+r>>1; ll ans=0; if(x>mid){ ans+=query(t<<1|1,mid+1,r); }else if(y<=mid){ ans+=query(t<<1,l,mid); }else{ ans+=query(t<<1,l,mid); ans+=query(t<<1|1,mid+1,r); } ans%=M; return ans; } int main(){ n=read();M=read(); for (int i=1;i<=n;i++){ a[i]=read(); } build(1,1,n); m=read(); while (m--){ op=read();x=read();y=read(); if (op==1){ cheng(1,1,n,read()); }else if (op==2){ jia(1,1,n,read()); }else{ printf("%lld\n",query(1,1,n)); } } return 0; }