题意:
在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; }