如題,已知乙個數列,你需要進行下面三種操作:
將某區間每乙個數乘上 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;
乘:乘稍微要改變一下修改6首先把值乘要乘的數是沒問題的,
此時lazy乘法標記乘要乘的數也是沒問題的
但是lazy加法標記也要乘
應為此時兒子感到他們父親乘上乙個數,於是自己也跟著乘上乙個數(是同理,過程用lazy標記完成)
換過來說就是你的值改變了,變大了,那和你有關的值也要變大
舉例:
/ \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項的初始值。接下來...