BZOJ2957 楼房重建
Description
小A的楼房外有一大片施工工地,工地上有N
栋待建的楼房。每天,这片工地上的房子拆了又建、建了又拆。他经常无聊地看着窗外发呆,数自己能够看到多少栋房子。
为了简化问题,我们考虑这些事件发生在一个二维平面上。小A
在平面上(0,0)
点的位置,第i
栋楼房可以用一条连接(i,0)
和(i,Hi)
的线段表示,其中Hi
为第i
栋楼房的高度。如果这栋楼房上任何一个高度大于0
的点与(0,0)
的连线没有与之前的线段相交,那么这栋楼房就被认为是可见的。
施工队的建造总共进行了M
天。初始时,所有楼房都还没有开始建造,它们的高度均为0
。在第i
天,建筑队将会将横坐标为Xi
的房屋的高度变为Yi
(高度可以比原来大---修建,也可以比原来小---拆除,甚至可以保持不变---建筑队这天什么事也没做)。请你帮小A数数每天在建筑队完工之后,他能看到多少栋楼房?
Solution
先转换问题,由于小A站在原点,那么一幢房子能被看到的条件就是其斜率k
严格大于之前的所有的房子的斜率, 那么问题就变成了求一段区间内值严格大于之前的所有点的点的数目。
求一段区间的值, 直接采用线段树,考虑两个区间如何合并。在只考虑当前区间时,首先其左区间的答案一定要被统计下来。 考虑加上左端点的限制之后如何计算。 如果当前左区间的左区间的最大值大于左区间的最大值, 那么先计算左区间的值,然后直接利用左区间计算右区间的值, 这样就转变为左区间对右区间可用限制。
如果右左区间的最大值比左区间要小, 那么直接计算以当前限制继续计算右端点即可。左区间算是废了。
(以下区间均指右儿子之所属)
考虑如何用已知条件计算出右区间的值。 因为当前区间的右区间是最新的值, 我们直接用右端点的答案减去右左区间的答案就可以。因为在更新当前区间时, 其值都是最新的,而这样算是为了解决右区间要考虑氘左区间的限制。
insp: 咕咕咕
Code
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#define rep(i, a, b) for(int i = (a), i##_end_ = (b); i <= i##_end_; ++i)
#define drep(i, a, b) for(int i = (a), i##_end_ = (b); i >= i##_end_; --i)
#define clar(a, b) memset(a, (b), sizeof(a))
int read() {
int x = 0, flag = 1;
char ch = getchar();
for (; !isdigit(ch); ch = getchar()) if(ch == '-') flag *= -1;
for (; isdigit(ch); ch = getchar()) x = x * 10 + ch - 48;
return x * flag;
}
void write(int x) {
if(x < 0) putchar('-'), x = -x;
if(x >= 10) write(x / 10);
putchar(x % 10 + 48);
}
const int Maxn = (int)1e6;
int n;
namespace SGMT_tree {
int tree[Maxn << 1]; double max[Maxn << 1];
#define lc(x) ((x) << 1)
#define rc(x) ((x) << 1 | 1)
#define ls rt << 1, l, mid
#define rs rt << 1 | 1, mid + 1, r
int calcLim(int rt, int l, int r, double val) {
if(l == r) return max[rt] > val;
int mid = (l + r) >> 1;
if(max[lc(rt)] <= val) return calcLim(rc(rt), mid + 1, r, val);
return calcLim(lc(rt), l, mid, val) + tree[rt] - tree[lc(rt)];
}
void modify(int rt, int l, int r, int pos, double val) {
if(l == r) {
max[rt] = val, tree[rt] = 1;
return ;
}
int mid = (l + r) >> 1;
if(pos <= mid) modify(ls, pos, val); else modify(rs, pos, val);
max[rt] = std :: max(max[lc(rt)], max[rc(rt)]);
tree[rt] = tree[lc(rt)] + calcLim(rc(rt), mid + 1, r, max[lc(rt)]);
}
}
void solve() {
rep(i, 1, read()) {
int pos = read(), val = read(); double k = 1.0 * val / pos;
SGMT_tree :: modify(1, 1, n, pos, k);
printf("%d\n", SGMT_tree :: tree[1]);
}
}
int main() {
n = read();
solve();
return 0;
}