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;
}
posted @ 2020-02-13 15:43  Mrzdtz220  阅读(74)  评论(0编辑  收藏  举报