P4198 樓房重建 線段樹

2021-10-17 03:18:19 字數 1521 閱讀 8147

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

)能看到幾條線。

把線變成斜率的話就是對於每個點求乙個往後比它大的第乙個點然後一直跳來做了。

線段樹的話主要是合併區間的時候比較麻煩,乙個暴力的想法是直接維護每個區間的序列,然後合併的時候乙個在另乙個上面二分,但是這樣合併的複雜度是o(l

en

)o(len)

o(len)

的。發現我們需要的只是在右區間的序列上二分而已,而右區間的序列是由它線段樹上的子樹得到,所以我們沒有必要真正的存下來維護的序列,可以直接在右邊的線段樹上二分。

大概的操作就是右邊分出來的左右兩個區間,如果左邊的區間最大值要比目前的值要小,那麼左邊區間沒有貢獻,直接到右邊。否則那麼這樣跳一定會到達左邊區間的最大值,然後就是遞迴求右邊區間的答案,這部分答案我們已經處理完了,直接累加然後遞迴作左區間即可。

這樣時間複雜度就是o(n

log⁡2n

)o(n\log^2 n)

o(nlog2n

)的了

#include

#include

#include

using

namespace std;

const

int n=

1e5+10;

int n,m,len[n<<2]

;double w[n<<2]

,a[n]

;int

pushup

(double val,

int x,

int l,

int r)

void

change

(int x,

int l,

int r,

int pos,

double val)

int mid=

(l+r)

>>1;

if(pos<=mid)

change

(x*2

,l,mid,pos,val)

;else

change

(x*2+1

,mid+

1,r,pos,val)

; w[x]

=max

(w[x*2]

,w[x*2+

1]);

len[x]

=len[x*2]

+pushup

(w[x*2]

,x*2+1

,mid+

1,r);}

intmain()

return0;

}

P4198 樓房重建

傳送門 很妙的思路 首先,我們可以把每一棟樓房轉化為它的頂部到原點這條直線的斜率,這樣就變成了從乙個序列中選出乙個最長上公升子串行 其實不是最長上公升子串行,不過可以這麼理解 考慮用線段樹來維護,對於每個區間,我們維護這個區間的最大值以及這個區間的答案,那麼最後的答案就是 ans 1 對於葉節點來說...

P4198 樓房重建

題意 n 棟待建的樓房,站在 0,0 點,對於樓房 i 來說,如果從原點能看到樓房的樓頂且沒有樓房阻擋,就算能看到該樓房 每次對於一座樓房可以增加高度和減小高度,每次修改後問最多能看到多少棟樓房?題解 線段樹維護區間斜率最大值,以及區間最長上公升子串行 即斜率遞增 的長度,難點在於區間的合併 對於每...

P4198 樓房重建(線段樹,維護LIS)

思路 實際上是在維護兩個值 區間最值mxmx mx和區間最長上公升子串行len lenle n。只不過這裡的最長上公升子串行有限制 要求能取盡量取,也就1 5 2 3 4的len lenle n是2不是4。想清楚了要維護的值,我們就要想怎麼維護。對於l en lenle n值,我們只需要對pus h...