生命曲線
時間限制: 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根柱子的高度在公升序排序後是否構成一段連續且上公升的序列。並且這些...