1. 程式人生 > >[線段樹模板題] 線段樹2

[線段樹模板題] 線段樹2

洛谷原題

第二次做 線段樹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; }