BZOJ 2957: 楼房重建
考虑线段树维护区间最大斜率以及区间答案
合并答案时左边的答案不会受到影响,而右边就要统计答案左边斜率最大值 \(k\) 的个数
递归进去求解,当区间最大斜率不大于这个 \(k\),那么答案为 \(0\) 直接返回,否则看左右儿子
若左儿子的最大斜率大于等于 \(k\),那么当前 \(ans[p]-ans[lp]\) 就是右儿子里比左儿子最大斜率大的答案,可以直接用,然后再加上递归左儿子的答案
若左儿子的最大斜率小于 \(k\),就直接递归统计右儿子的答案
每一次只会选一个儿子递归,所以合并复杂度是 \(O(\log n)\),总复杂度是 \(O(m\log ^2 n)\)
#include <bits/stdc++.h>
typedef double db;
const int N = 4e5 + 7;
struct Seg {
#define lp p << 1
#define rp p << 1 | 1
db mx[N];
int ans[N];
int count(int p, int l, int r, db k) {
if (l == r) return mx[p] > k;
if (mx[p] <= k) return 0;
int mid = l + r >> 1;
if (mx[lp] >= k) return ans[p] - ans[lp] + count(lp, l, mid, k);
return count(rp, mid + 1, r, k);
}
void update(int p, int l, int r, int pos, db k) {
if (l == r) {
mx[p] = k;
ans[p] = 1;
return;
}
int mid = l + r >> 1;
if (pos <= mid) update(lp, l, mid, pos, k);
else update(rp, mid + 1, r, pos, k);
mx[p] = std::max(mx[lp], mx[rp]);
ans[p] = ans[lp] + count(rp, mid + 1, r, mx[lp]);
}
} seg;
int main() {
int n, m;
scanf("%d%d", &n, &m);
for (int x, y; m--; ) {
scanf("%d%d", &x, &y);
seg.update(1, 1, n, x, (db)y/x);
printf("%d\n", seg.ans[1]);
}
return 0;
}