题意:
在x轴上有N栋房子,坐标为1~N。初始时每栋房子的高度为0(即还没建),之后有M天,每天可以修改一栋房子的高度。一个人站在原点向x轴正半轴看去,如果从原点到某栋楼楼顶的连线不与其他的楼相交,那么这人就看得到这栋楼。求每天可以看见几栋房子。N,M<=100000。
分析:
设第i栋房子的高度为a_i。如果一栋房子可以被看到,即其斜率a_i/i比其前面所有的房子的斜率都要大。那么现在问题变成了,每次修改一个数字,问此时有多少数字比之前的数字都要大。
一种比较显然的O(Nlog^2N)做法是用线段树套平衡树……但是写起来挺蛋疼。事实上我们可以只用线段树完成这个任务。
一个显而易见的结论是,这种数字的值是单调递增的。我们修改一个数只会对这个数后面的数造成影响。考虑线段树划分出来的若干线段。这里有两种情况:
1、某个线段中的最大值小于等于修改的数,那么这个线段的贡献为0,无需处理
2、否则我们将这个线段分成两个并单独考虑,如果左侧的最大值大于修改的数,那么是不影响右侧的贡献的,只需递归处理左侧;否则就变成了第一种情况
那么我们就可以用线段树来解决这个问题了……复杂度好像是O(Nlog^2N)?但是常数巨小无比,而且非常好写……
代码:(calc那里实现需要注意一下……虽然我也不知道为啥)
//BZOJ2957; rebuild (2013THU Training Day 1); Segment Tree
#include <cstdio>
#include <algorithm>
using namespace std;
inline int read() {
static int r;
static char c;
r = 0, c = getchar();
while (c < '0' || c > '9') c = getchar();
while (c >= '0' && c <= '9') r = r * 10 + (c - '0'), c = getchar();
return r;
}
#define N 100000
struct node {
double v;
int s;
} tree[N * 5];
#define ls(x) ((x) << 1)
#define rs(x) ((x) << 1 | 1)
#define s(x) (tree[x].s)
#define v(x) (tree[x].v)
int calc(int x, int l, int r, double v) {
if (l == r) return (int)(v(x) > v);
int mid = l + r >> 1;
if (v(ls(x)) <= v) return calc(rs(x), mid + 1, r, v);
return s(x) - s(ls(x)) + calc(ls(x), l, mid, v);
}
void modify(int x, int l, int r, int p, double v) {
if (l == r) {
v(x) = v, s(x) = 1;
return ;
}
int mid = l + r >> 1;
if (p <= mid) modify(ls(x), l, mid, p, v);
else modify(rs(x), mid + 1, r, p, v);
v(x) = max(v(ls(x)), v(rs(x)));
s(x) = s(ls(x)) + calc(rs(x), mid + 1, r, v(ls(x)));
}
int n, m, x, y;
int main(int argc, char* argv[]) {
#ifdef KANARI
freopen("input.txt", "r", stdin);
freopen("output.txt", "w", stdout);
#endif
n = read(), m = read();
while (m--) {
x = read(), y = read();
modify(1, 1, n, x, (double)y / x);
printf("%d\n", s(1));
}
fclose(stdin);
fclose(stdout);
return 0;
}