線段樹(洛谷P3373模板2)

2021-09-26 11:02:51 字數 4372 閱讀 7987

日常膜拜dalao :財神萬歲!!!!!!!!!!!!!!!!

話說這個線段樹今天折磨了我五個小時然後終於發現少打了乙個2.。。。

(離開學還有4天然而作業一字未動絲毫不慌 ing)

原題連線:

洛谷線段樹模板2

要求:如題,已知乙個數列,你需要進行下面三種操作:

1.將某區間每乙個數乘上x

2.將某區間每乙個數加上x

3.求出某區間每乙個數的和

輸入格式

第一行包含三個整數n、m、p,分別表示該數列數字的個數、操作的總個數和模數。

第二行包含n個用空格分隔的整數,其中第i個數字表示數列第i項的初始值。

接下來m行每行包含3或4個整數,表示乙個操作,具體如下:

操作1: 格式:1 x y k 含義:將區間[x,y]內每個數乘上k

操作2: 格式:2 x y k 含義:將區間[x,y]內每個數加上k

操作3: 格式:3 x y 含義:輸出區間[x,y]內每個數的和對p取模所得的結果

樣例:輸入:

5 5 38

1 5 4 2 3

2 1 4 1

3 2 5

1 2 4 2

2 3 5 5

3 1 4

輸出:17

2查詢複雜度nlogn,修改複雜度採用懶標記可以優化到logn。

建樹:按自己的需求可以開結構體(然而我並沒用結構體,然後同於建二叉樹的操作(遞迴),先建左兒子和右兒子,再建自己

voidbt(

int now,

int l,

int r)

else

tree[now]

%=p;

return

;}

區間長度+k

使用add陣列,標記了add[i] +=k , 在計算長度時將i 號節點的標記計算進去就可以了,這樣避免了對每乙個節點的修改

區間長度* k

同上,陣列名為add2 .

注意事項

無論是加還是乘,修改方向都是自上而下的。

乘法優先,即規定好

兒子的值=此刻兒子的值乘爸爸的乘法lazytag+兒子的區間長度乘爸爸的加法lazytag

(ak+bkck+…+nk)=k(a+b+c+…+n)所以此刻兒子值乘 k相當於它的每乙個下面的都乘以k,然後再加上加法的值。

然而如果你加法優先的話,乘法和加法會黏成一團。。

在每次change兒子前先進行一次下放操作,把父親節點的標記都清掉,移到兒子節點上,並對兒子節點的值進行更新。

(話說我為啥不說單點修改呢?因為這其實就是對於區間長度為n的操作的一部分

( n ==1))

下放與更新操作

void

pushdown

(int now,

int l,

int r)

//下放

void

change

(int x,

int y,

int k,

int l,

int r,

int now)

//乘法

pushdown

(now,l,r)

;int mid=

(l+r)

>>1;

change

(x,y,k,l,mid,now*2)

;change

(x,y,k,mid+

1,r,now*2+

1); tree[now]

=(tree[now*2]

+tree[now*2+

1])%p;

}void

change2

(int x,

int y,

int k,

int l,

int r,

int now)

//加法

pushdown

(now,l,r)

;int mid=

(l+r)

>>1;

change2

(x,y,k,l,mid,now*2)

;change2

(x,y,k,mid+

1,r,now*2+

1); tree[now]

=(tree[now*2]

+tree[now*2+

1])%p;

return

;}

查詢操作

這個貌似沒啥好講的,,就是當前區間如果全在查詢區間內就累計。。

long

long

ask(

int askl,

int askr,

int now,

int l,

int r)

pushdown

(now,l,r)

;int mid=

(l+r)

>>1;

return

(ask

(askl,askr,now*

2,l,mid)

+ask

(askl,askr,now*2+

1,mid+

1,r)

)%p;

}

#include

//線段樹 洛谷p3373模板

#include

#include

#include

#define maxn 1000001

using namespace std;

int n,m,p;

long

long a[maxn]

,temp=0;

long

long tree[

2*maxn]

,add[

2*maxn]

,add2[

2*maxn]

;voidbt(

int now,

int l,

int r)

else

tree[now]

%=p;

return;}

void

pushdown

(int now,

int l,

int r)

long

long

ask(

int askl,

int askr,

int now,

int l,

int r)

pushdown

(now,l,r)

;int mid=

(l+r)

>>1;

return

(ask

(askl,askr,now*

2,l,mid)

+ask

(askl,askr,now*2+

1,mid+

1,r)

)%p;

}void

change

(int x,

int y,

int k,

int l,

int r,

int now)

pushdown

(now,l,r)

;int mid=

(l+r)

>>1;

change

(x,y,k,l,mid,now*2)

;change

(x,y,k,mid+

1,r,now*2+

1); tree[now]

=(tree[now*2]

+tree[now*2+

1])%p;

}void

change2

(int x,

int y,

int k,

int l,

int r,

int now)

pushdown

(now,l,r)

;int mid=

(l+r)

>>1;

change2

(x,y,k,l,mid,now*2)

;change2

(x,y,k,mid+

1,r,now*2+

1); tree[now]

=(tree[now*2]

+tree[now*2+

1])%p;

return;}

inline

long

long

read()

return s;

}int

main()

if(b==2)

//jia

if(b==3)

}return0;

}

洛谷P3373 模板 線段樹2

這題有毒啊,敲了我一晚上加一早上,總算a了。由於有加和乘兩個操作,要用2個lazy陣列。核心難點就是2個lazy陣列會相互影響。因為乘影響加,加不影響乘,所以我們先算乘。include include include include include include include include i...

洛谷 P3373 模板 線段樹 2

如題,已知乙個數列,你需要進行下面三種操作 1.將某區間每乙個數乘上x 2.將某區間每乙個數加上x 3.求出某區間每乙個數的和 第一行包含三個整數n m p,分別表示該數列數字的個數 操作的總個數和模數。第二行包含n個用空格分隔的整數,其中第i個數字表示數列第i項的初始值。接下來m行每行包含3或4個...

洛谷 P3373 模板 線段樹 2

洛谷 p3373 模板 線段樹 2 已知乙個數列,你需要進行下面三種操作 1.將某區間每乙個數乘上x 2.將某區間每乙個數加上x 3.求出某區間每乙個數的和 思路 兩個lazy標記 add和mul,怎麼下放?我遇到的問題 很多題解上來就說先乘後加比先加後乘好處理。我疑惑 如果有兩個lazy標記,不知...