P3373 模板 線段樹 2

2021-10-04 01:40:05 字數 4838 閱讀 5405

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

將某區間每乙個數乘上 x

將某區間每乙個數加上 x

求出某區間每乙個數的和

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

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

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

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

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

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

輸出包含若干行整數,即為所有操作 3 的結果。
輸入
5538

1542

3214

1325

1242

2355

314

17

2

【資料範圍】

對於30%的資料:n≤8,m≤10

對於70%的資料: n≤10^3 ,m≤10^4

對於100%的資料: n≤10^5 ,m≤10^5

首先看到這道題,我們想到的是用lazy標記來完成這道題。

可是它有乘法,怎麼辦呢?

我們不妨設兩個lazy,乙個用來存加法,乙個存乘法

這時講到我們的核心部分:down(向下傳遞)

我們一般的down只有加法,這次多了乘法

我們規定乘法優先:

a[k*2]

.flag2=

(a[k*2]

.flag2*a[k]

.flag2)

%p;a[k*2+

1].flag2=

(a[k*2+

1].flag2*a[k]

.flag2)

%p;

後面再算加法:

a[k*2]

.flag1=

(a[k*2]

.flag1*a[k]

.flag2+a[k]

.flag1)

%p;//乘法遞增加法,在下面我會講的(章節:加和乘修改——乘)

a[k*2+

1].flag1=

(a[k*2+

1].flag1*a[k]

.flag2+a[k]

.flag1)

%p;

最後初始化

a[k]

.flag1=0;

a[k]

.flag2=

1;

這時,問題來了:我們怎麼計算它更改後的數值呢?

可以分成兩部分:

兒子的值乘父親的lazy乘法標記的值

原因:應為是區間乘法,所以就乘

兒子的區間長度乘父親lazy加法標記

原因:和普通線段樹加法同理

把兩個合起來就行了:

a[k*2]

.w=(a[k*2]

.w*a[k]

.flag2+

(a[k*2]

.r-a[k*2]

.l+1

)*a[k]

.flag1)

%p;a[k*2+

1].w=(a[k*2+

1].w*a[k]

.flag2+

(a[k*2+

1].r-a[k*2+

1].l+1

)*a[k]

.flag1)

%p;

有了上面的down,我們接下來就很容易了

加:
加法和普通線段樹沒有區別:

if

(a[k]

.l>=x&&a[k]

.r<=y)

down

(k);

int mid=

(a[k]

.l+a[k]

.r)/2;

if(x<=mid)

changet1

(k*2

,x,y,z);if

(y>mid)

changet1

(k*2+1

,x,y,z)

; a[k]

.w=(a[k*2]

.w+a[k*2+

1].w)%p;

乘:
乘稍微要改變一下修改

首先把值乘要乘的數是沒問題的,

此時lazy乘法標記乘要乘的數也是沒問題的

但是lazy加法標記也要乘

應為此時兒子感到他們父親乘上乙個數,於是自己也跟著乘上乙個數(是同理,過程用lazy標記完成)

換過來說就是你的值改變了,變大了,那和你有關的值也要變大

舉例:

6

/ \3 4

6此時標記是2,此時它乘了2,那它的兒子3,4也會改變,所以↑↑↑————(我的理解)(應該是這樣吧)

那乘法的更改也顯而易見:

if

(a[k]

.l>=x&&a[k]

.r<=y)

down

(k);

int mid=

(a[k]

.l+a[k]

.r)/2;

if(x<=mid)

changet2

(k*2

,x,y,z);if

(y>mid)

changet2

(k*2+1

,x,y,z)

; a[k]

.w=(a[k*2]

.w+a[k*2+

1].w)%p;

最後綜合在一起:

#

include

#include

using

namespace std;

long

long m,n,p,ans;

struct

node

a[4000001];

void

buill

(int k,

int l,

int r)

buill

(k*2

,l,(l+r)/2

);buill

(k*2+1

,(l+r)/2

+1,r);

a[k]

.w=(a[k*2]

.w+a[k*2+

1].w)%p;

}void

down

(int k)

void

changet2

(int k,

int x,

int y,

int z)

down

(k);

int mid=

(a[k]

.l+a[k]

.r)/2;

if(x<=mid)

changet2

(k*2

,x,y,z);if

(y>mid)

changet2

(k*2+1

,x,y,z)

; a[k]

.w=(a[k*2]

.w+a[k*2+

1].w)%p;

}void

changet1

(int k,

int x,

int y,

int z)

down

(k);

int mid=

(a[k]

.l+a[k]

.r)/2;

if(x<=mid)

changet1

(k*2

,x,y,z);if

(y>mid)

changet1

(k*2+1

,x,y,z)

; a[k]

.w=(a[k*2]

.w+a[k*2+

1].w)%p;

}void

findt

(int k,

int x,

int y)

down

(k);

int mid=

(a[k]

.l+a[k]

.r)/2;

if(x<=mid)

findt

(k*2

,x,y);if

(y>mid)

findt

(k*2+1

,x,y);}

intmain()

else

if(g==2)

else

}}

P3373 模板 線段樹2

如題,已知乙個數列,你需要進行下面三種操作 1.將某區間每乙個數乘上x 2.將某區間每乙個數加上x 3.求出某區間每乙個數的和 include include using namespace std const int maxn 100005 int n,m,p long long arr maxn...

P3373 模板 線段樹 2

ac 這裡的延遲標記要開兩個,分別記錄加法的值和乘法的值,但是乘法和加法的優先順序不一樣,不規定他們的順序的話會有錯誤,所以可以規定乘法優先,即規定好該結點的值等於該節點的值 父節點的乘法延遲標記的值 父節點加法延遲標記的值 區間長度,即,sum num 2 sum num 2 add num wc...

P3373 模板 線段樹 2

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