【線段樹】【P4198】 樓房重建
Description
小A在平面上(0,0)點的位置,第i棟樓房可以用一條連接(i,0)和(i,Hi)的線段表示,其中Hi為第i棟樓房的高度。如果這棟樓房上任何一個高度大於0的點與(0,0)的連線沒有與之前的線段相交,那麽這棟樓房就被認為是可見的。
施工隊的建造總共進行了M天。初始時,所有樓房都還沒有開始建造,它們的高度均為0。在第i天,建築隊將會將橫坐標為Xi的房屋的高度變為Yi(高度可以比原來大—修建,也可以比原來小—拆除,甚至可以保持不變—建築隊這天什麽事也沒做)。請你幫小A數數每天在建築隊完工之後,他能看到多少棟樓房?
Input
第一行兩個正整數 \(n,m\)
下面 \(m\) 行每行兩個正整數代表 \(x_i,y_i\)
Output
每次操作後輸出一行一個數字代表當前答案
Hint
\(1~\leq~x_i~\leq~n~\leq~100000~,~1~\leq~m~\leq~100000, 0~\leq~y_i~\leq~10^9\)
Solution
我們考慮將問題放到線段樹上。
顯然一棟樓能被看到當且僅當他和原點之間的所有樓房中沒有一棟樓的樓頂到原點的斜率不小於它的樓頂到原點的斜率。
對每個區間維護區間最大斜率和只考慮當前區間的ans。
考慮合並兩個區間的情況。
如果左區間的最大斜率不小於右區間的最大斜率,那麽顯然右區間被完全擋住,直接上傳左區間信息。
否則考慮左區間對右區間的限制
考慮右區間的左右兩個子樹,當左子樹的最大斜率不大於左區間的最大斜率時,左子樹被完全擋住,遞歸處理右子樹的答案
當左子樹的斜率大於左區間的斜率時,左子樹對右子樹的限制大於左區間對右子樹的限制,即我們無需考慮左區間對右子樹的限制,於是我們遞歸計算左子樹的答案,加上左子樹對右子樹的限制就是右區間做出的貢獻。而左子樹對右子樹的限制就是右區間的答案剪去左子樹的答案。
時間復雜度 \(O(n~log^2n)\)。
Code
#include <cstdio> #ifdef ONLINE_JUDGE #define freopen(a, b, c) #define printtime() #else #include <ctime> #define printtime() printf("Times used = %ld ms\n", clock()) #endif #define ci const int #define cl const long long typedef long long int ll; namespace IPT { const int L = 1000000; char buf[L], *front=buf, *end=buf; char GetChar() { if (front == end) { end = buf + fread(front = buf, 1, L, stdin); if (front == end) return -1; } return *(front++); } } template <typename T> inline void qr(T &x) { char ch = IPT::GetChar(), lst = ' '; while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar(); while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar(); if (lst == '-') x = -x; } template <typename T> inline void ReadDb(T &x) { char ch = IPT::GetChar(), lst = ' '; while ((ch > '9') || (ch < '0')) lst = ch, ch = IPT::GetChar(); while ((ch >= '0') && (ch <= '9')) x = x * 10 + (ch ^ 48), ch = IPT::GetChar(); if (ch == '.') { ch = IPT::GetChar(); double base = 1; while ((ch >= '0') && (ch <= '9')) x += (ch ^ 48) * ((base *= 0.1)), ch = IPT::GetChar(); } if (lst == '-') x = -x; } namespace OPT { char buf[120]; } template <typename T> inline void qw(T x, const char aft, const bool pt) { if (x < 0) {x = -x, putchar('-');} int top=0; do {OPT::buf[++top] = static_cast<char>(x % 10 + '0');} while (x /= 10); while (top) putchar(OPT::buf[top--]); if (pt) putchar(aft); } const int maxn = 100010; struct Info { int v; double mh; Info() {v = mh = 0;} }; struct Tree { Tree *ls, *rs; int l, r; Info v; Tree() {ls = rs = NULL;} int calc(double _v) { if (this->v.mh <= _v) return 0; if (!this->ls) return 1; if (this->ls->v.mh <= _v) return this->rs->calc(_v); else return this->ls->calc(_v) + this->v.v - this->ls->v.v; } void pushup() { if (this->ls->v.mh >= this->rs->v.mh) { this->v = this->ls->v; return; } else { this->v.mh = this->rs->v.mh; this->v.v = this->ls->v.v + this->rs->calc(this->ls->v.mh); } } }; Tree *rot; int n, m; void build(Tree*, ci, ci); void update(Tree*, ci, const double); int main() { freopen("1.in", "r", stdin); qr(n); qr(m); rot = new Tree; build(rot, 0, n); int a, b; while (m--) { a = b = 0; qr(a); qr(b); update(rot, a, 1.0 * b / a); qw(rot->v.v - 1, '\n', true); } printtime(); } void build(Tree *u, ci l, ci r) { u->l = l; u->r = r; if (l == r) {u->v.v = 1; return;} int mid = (l + r) >> 1; build(u->ls = new Tree, l, mid); build(u->rs = new Tree, mid + 1, r); u->pushup(); } void update(Tree *u, ci p, const double v) { if ((u->l > p) || (u->r < p)) return; if (u->l == u->r) { u->v.mh = v; return; } update(u->ls, p, v); update(u->rs, p, v); u->pushup(); }
【線段樹】【P4198】 樓房重建