upc 生命曲線 線段樹 lazy

2021-10-05 03:43:12 字數 4978 閱讀 2142

生命曲線

時間限制: 2 sec 記憶體限制: 128 mb

題目描述

mr.l最近得了妄想症,他對自己生命的歷史和未來產生了一些幻覺。

他的眼前浮現出了自己的生命價值曲線。這條生命線不斷地變化,使他不斷懷疑著自己存在的意義……

mr.l的生命價值曲線可以被簡化為乙個代表著mr.l的每個階段的生命價值的序列。其中第i個階段的生命價值為vi。

當他預感自己成功或失敗時,他的生命線上會遞增或遞減地增加或減少一些價值。他認為這些價值的變化量是形成等差數列的。

還有時候,他預感到自己的大起大落,此時他一段時間的生命價值會發生反轉。

具體來說,若[l,r]時間內的生命價值發生了反轉,則其中的每個價值都會變成 它原先價值的相反數。

他對自己在某段時間內的生命價值很感興趣,所以還想求出某些時間段內的生命價值總和。

輸入第一行兩個整數n和m。

第二行n個數,表示初始序列v。

以下m行每行3或5個數,表示每個操作。

1 l r a1 d:將第l個到第r個元素加上乙個等差數列。

具體來說,對於i∈[l,r],vi=vi+ai-l+1 ,其中ai=a1+(i - 1)d

2 l r:求第l個到第r個數的和。

3 l r:第l個到第r個數反轉(意義見上)。

輸出對於每個2操作輸出一行乙個數即第l個到第r個數的和。

樣例輸入 copy

5 52 4 4 3 5

1 3 5 2 1

2 2 4

1 2 4 3 2

3 2 5

2 1 4

樣例輸出 copy

16-29

提示樣例解釋:

初始生命價值為2 4 4 3 5

1 3 5 2 1操作後價值為2 4 6 6 9

2 2 4輸出4+6+6=16

1 2 4 3 2操作後價值為2 7 11 13 9

3 2 5操作後價值為2-7-11-13-9

2 1 4輸出2-7-11-13=-29

對於100%的資料,n,m≤5×10^5,vi,|a1|,|d|≤1000

注意答案可能不在[-2 ^ 31,2 ^ 31-1]的範圍內

乙個算是比較裸的 線段樹+區間修改 的題了,然鵝太菜了,知道怎麼做了還是調了半小時,細節處理的實在不是很好。

題中分為三個操作:

(1)把 l ~ r 區間加上乙個等差數列

(2)把 l ~ r 區間的數取反

(3)求 l ~ r 的區間數的總和

很明顯結構體中需要存乙個總和 sum,以及乙個lazy標記 fz 用來表示將數反轉。

第二個和第三個比較簡單,直接lazy往下傳就行了,主要是在於處理第乙個加等差數列上。

對於操作 (1)

1.首先要知道等差數列+等差數列=等差數列

2.當修改時候區間不需要進行**的時候,直接把 a d sum 加到對應的結點上即可。

3.當區間需要進行**的時候,可以把它看成兩部分,第一部分就是首項為a,公差為d的等差數列。而第二部分的公差仍然為d,首項就需要a+len*d,len為前面一部分的長度,具體可以看**。

pushdown的時候要先看fz是否標記,之後再把 a d sum 傳給下面的結點。

再就是注意一下算等差數列的和的時候要仔細點即可。。

**還是分開看吧,全部**在最後。

1.定義

int n,m;

int w[n]

;struct node

tr[n*4]

;

2.pushdown + pushup + 建樹

個人感覺起的名字還是比較能看懂的吧。。

void

pushup

(int u)

void

pushdown

(int u)

if(tr[u]

.a!=

0||tr[u]

.d!=0)

}void

build

(int u,

int l,

int r)

;return;}

tr[u]=;

int mid=tr[u]

.l+tr[u]

.r>>1;

build

(u<<

1,l,mid)

,build

(u<<1|

1,mid+

1,r)

;pushup

(u);

}

3.區間加等差數列

這裡注意一下求 t1 的時候 取max( tr[u].l , l ) ,這倆的位置不確定,手畫一下圖就可以看出來了。

void

modify1

(int u,

int l,

int r,ll a,ll d)

pushdown

(u);

int mid=tr[u]

.l+tr[u]

.r>>1;

if(r<=mid)

modify1

(u<<

1,l,r,a,d)

;else

if(l>mid)

modify1

(u<<1|

1,l,r,a,d)

;else

pushup

(u);

}

4.區間數反轉

void

modify2

(int u,

int l,

int r)

pushdown

(u);

int mid=tr[u]

.l+tr[u]

.r>>1;

if(l<=mid)

modify2

(u<<

1,l,r);if

(r>mid)

modify2

(u<<1|

1,l,r)

;pushup

(u);

}

5.查詢區間和

ll query

(int u,

int l,

int r)

全部**:

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include

#define x first

#define y second

using

namespace std;

typedef

long

long ll;

typedef pair<

int,

int> pii;

const

int n=

500010

,mod=

1e9+

7,inf=

0x3f3f3f3f

;const

double eps=

1e-6

;int n,m;

int w[n]

;struct node

tr[n*4]

;void

pushup

(int u)

void

pushdown

(int u)

if(tr[u]

.a!=

0||tr[u]

.d!=0)

}void

build

(int u,

int l,

int r)

;return;}

tr[u]=;

int mid=tr[u]

.l+tr[u]

.r>>1;

build

(u<<

1,l,mid)

,build

(u<<1|

1,mid+

1,r)

;pushup

(u);

}void

modify1

(int u,

int l,

int r,ll a,ll d)

pushdown

(u);

int mid=tr[u]

.l+tr[u]

.r>>1;

if(r<=mid)

modify1

(u<<

1,l,r,a,d)

;else

if(l>mid)

modify1

(u<<1|

1,l,r,a,d)

;else

pushup

(u);

}void

modify2

(int u,

int l,

int r)

pushdown

(u);

int mid=tr[u]

.l+tr[u]

.r>>1;

if(l<=mid)

modify2

(u<<

1,l,r);if

(r>mid)

modify2

(u<<1|

1,l,r)

;pushup

(u);

}ll query

(int u,

int l,

int r)

intmain()

else

if(op==2)

else

modify2(1

,l,r);}

return0;

}

例題 線段樹 lazy

1 1 lazy思想 對整個結點進行的操作,先在結點上做標記,而並非真正執行,直到根據查詢操作的需要分到下層。2 延遲標記 lazy 如果需要對乙個區間中每乙個葉結點進行操作,我們不妨先別忙著操作,而是在所有大區間上做乙個標記,下一次遇到或要用到時,再進行處理 標記傳遞 達到減少操作次數,提高線段樹...

線段樹 lazy標記

每個節點代表區間 唯一根節點,也就是全部區間 葉節點是長度為1的子區間,也就是所代表陣列上的乙個點 const int maxn 1e3 struct segmenttree e 4 maxn 建樹 void build int p,int l,int r int mid l r 2 build p...

小A盜墓 upc(線段樹)

upc問題 e 小a盜墓 時間限制 5 sec 記憶體限制 128 mb 題目描述 小a終於通過了保安的考驗,來到了古墓門前,古墓門前有n根柱子,第i根柱子的高度是整數。古墓的門上會彈出一些暗號,機智小a猜到這個暗號表示詢問第l到第r根柱子的高度在公升序排序後是否構成一段連續且上公升的序列。並且這些...