1. 程式人生 > >[BZOJ29957] 樓房重建 - 線段樹

[BZOJ29957] 樓房重建 - 線段樹

什麽 upd ostream amp 一行 print sin main online

2957: 樓房重建

Time Limit: 10 Sec Memory Limit: 256 MB
Submit: 3294 Solved: 1554
[Submit][Status][Discuss]

Description

  小A的樓房外有一大片施工工地,工地上有N棟待建的樓房。每天,這片工地上的房子拆了又建、建了又拆。他經常無聊地看著窗外發呆,數自己能夠看到多少棟房子。
  為了簡化問題,我們考慮這些事件發生在一個二維平面上。小A在平面上(0,0)點的位置,第i棟樓房可以用一條連接(i,0)和(i,Hi)的線段表示,其中Hi為第i棟樓房的高度。如果這棟樓房上任何一個高度大於0的點與(0,0)的連線沒有與之前的線段相交,那麽這棟樓房就被認為是可見的。

  施工隊的建造總共進行了M天。初始時,所有樓房都還沒有開始建造,它們的高度均為0。在第i天,建築隊將會將橫坐標為Xi的房屋的高度變為Yi(高度可以比原來大---修建,也可以比原來小---拆除,甚至可以保持不變---建築隊這天什麽事也沒做)。請你幫小A數數每天在建築隊完工之後,他能看到多少棟樓房?

Input

  第一行兩個正整數N,M
  接下來M行,每行兩個正整數Xi,Yi

Output


  M行,第i行一個整數表示第i天過後小A能看到的樓房有多少棟

Sample Input


3 4
2 4
3 6
1 1000000000
1 1

Sample Output


1
1
1
2
數據約定
  對於所有的數據1<=Xi<=N,1<=Yi<=10^9
N,M<=100000

題解: 那線段樹維護區間的最大斜率和斜率最長上升子序列的長度; 甚至建樹都不需要; 唯一的問題就是update,我們好像不可以O(1)合並。 我們把一個序列分為左右兩端記為L, R; 如果R的最大斜率小於L的最大斜率,那麽有區間直接就不用考慮了; 如果R的最大斜率大於L的最大斜率,那麽我們二分R區間,像這樣遞歸下去;
Code:
#include <iostream>
#include 
<cstdio> using namespace std; struct segment { int len; long double mx; }t[400010]; #define ls(o) o<<1 #define rs(o) o<<1|1 #define mx(o) t[o].mx #define len(o) t[o].len inline int count(int l, int r, int o, long double hi) { if (l == r) return mx(o) > hi; int mid = l + r >> 1; if (mx(ls(o)) <= hi) return count(mid + 1, r, rs(o), hi); return len(o) - len(ls(o)) + count(l, mid, ls(o), hi); } inline void change(int l, int r, int o, int to, long double k) { if (l == r) { len(o) = 1; mx(o) = k; return ; } int mid = l + r >> 1; if (to <= mid) change(l, mid, ls(o), to, k); else change(mid + 1, r, rs(o), to, k); mx(o) = max(mx(ls(o)), mx(rs(o))); len(o) = len(ls(o)) + count(mid + 1, r, rs(o), mx(ls(o))); } int main() { int n, m; scanf("%d%d", &n, &m); while (m--) { int x, y; scanf("%d%d", &x, &y); change(1, n, 1, x, (long double)y / x); printf("%d\n", len(1)); } return 0; }

[BZOJ29957] 樓房重建 - 線段樹