題目描述
小 a 的樓房外有一大片施工工地,工地上有 n 棟待建的樓房。每天,這片工地上的房子拆了又建、建了又拆。他經常無聊地看著窗外發呆,數自己能夠看到多少棟房子。輸入格式為了簡化問題,我們考慮這些事件發生在乙個二維平面上。小 a 在平面上(0,0) 點的位置,第 i 棟樓房可以用一條連線 (i,0) 和 (i,hi)的線段表示,其中 hi為第 i 棟樓房的高度。如果這棟樓房上任何乙個高度大於 0 的點與(0,0)的連線沒有與之前的線段相交,那麼這棟樓房就被認為是可見的。
施工隊的建造總共進行了 m 天。初始時,所有樓房都還沒有開始建造,它們的高度均為 0。在第 i 天,建築隊將會將橫座標為 xi的房屋的高度變為 yi(高度可以比原來大—修建,也可以比原來小—拆除,甚至可以保持不變—建築隊這天什麼事也沒做)。請你幫小 a 數數每天在建築隊完工之後,他能看到多少棟樓房?
第一行兩個正整數 n,m。輸出格式接下來 m 行,每行兩個正整數 xi,yi 。
m 行,第 i 行乙個整數表示第 i 天過後小 a 能看到的樓房有多少棟。輸入輸出
輸入說明/提示3 42 4
3 61 1000000000
1 1輸出11
12
對於100%的資料,1≤xi≤n,1≤yi≤10^9, 1≤n,m≤10^5。題目分析
高的樓房會擋住後面低的樓房。但遮擋並不僅僅是簡單的大於後面的就會遮擋,比如:2位置上高度為4的樓房可以擋住4位置上高度為8的樓房。仔細思考不難發現:遮擋是基於斜率的,即:後面的樓房如果不會被前面的樓房遮擋,那麼後面這棟樓房的斜率一定大於前面所有樓房斜率。
要想快速的維護最大值,可以用線段樹來維護。線段樹中維護兩個資訊:每段的斜率最大值slo和從當前端的左邊向右看能看到多少棟樓樓len。
最大值slo比較的好維護,每段只需要取左右子段的最大值即可。
我們主要來看一下len如何來維護:
首先我們需要乙個函式getlen()來求出在前面有乙個斜率為lmlm
lm的遮擋物下,這一段能夠露出多少棟樓來(這個函式的寫法具體可以看**)。
有了這個函式後,就可以維護len了,本段的len=左子段的len+getlen(右子段 , 左子段的最大斜率)。
**如下
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define ll long long
#define pii pair
#define x first
#define y second
using
namespace std;
const
int n=
1e5+
5,inf=
0x3f3f3f3f
;struct nodetr[n*4]
;double a[n]
;//存某個位置樓房的斜率
intgetlen
(int u,
double lm)
//在前面有乙個斜率為lm的遮擋下,u段能夠露出多少棟樓
void
pushup
(int u)
void
build
(int u,
int l,
int r)
//建樹;if
(l==r)
return
;int mid=l+r>>1;
build
(u<<
1,l,mid)
,build
(u<<1|
1,mid+
1,r);}
void
modify
(int u,
int x,
int c)
//單點修改,將x位置的高度改為c
else
}int
main()
return0;
}
P4198 樓房重建
傳送門 很妙的思路 首先,我們可以把每一棟樓房轉化為它的頂部到原點這條直線的斜率,這樣就變成了從乙個序列中選出乙個最長上公升子串行 其實不是最長上公升子串行,不過可以這麼理解 考慮用線段樹來維護,對於每個區間,我們維護這個區間的最大值以及這個區間的答案,那麼最後的答案就是 ans 1 對於葉節點來說...
P4198 樓房重建
題意 n 棟待建的樓房,站在 0,0 點,對於樓房 i 來說,如果從原點能看到樓房的樓頂且沒有樓房阻擋,就算能看到該樓房 每次對於一座樓房可以增加高度和減小高度,每次修改後問最多能看到多少棟樓房?題解 線段樹維護區間斜率最大值,以及區間最長上公升子串行 即斜率遞增 的長度,難點在於區間的合併 對於每...
P4198 樓房重建 線段樹
n nn條線,開始時第i ii條是 i,0 i,0 i,0 的乙個點。每次有操作把第x xx條線變成 x,0 x,0 x,0 到 x,y x,y x,y 然後求從 0,0 0,0 0,0 能看到幾條線。把線變成斜率的話就是對於每個點求乙個往後比它大的第乙個點然後一直跳來做了。線段樹的話主要是合併區間...