Loading

楼房重建线段树

维护前缀最大值个数。

pushup 操作进行修改。

定义 solve(x, lim) 为前面这个区间的最大值为 lim\(x\) 支配的区间产生的贡献。

如果 \(x\) 的最大值已经小于 \(lim\),显然没有贡献。

考虑 \(x\) 的左儿子,如果左儿子的最大值大于 \(lim\) 直接递归左二子查询,此时右儿子的答案不受影响。

如果左儿子最大值小于 \(lim\) 的话,贡献显然为0,递归右儿子查询。

\(solve\) 每次只会递归一个儿子,一共 \(O(\log n)\) 层,复杂度 \(O(\log n)\)

总复杂度 \(O(n\log ^ 2 n)\)

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 5, INF = 0x3f3f3f3f;
const LL mod = 998244353;

int n, m;

struct Segment_Tree {
	struct Node {
		int l, r, val;
		double maxv;
		#define l(x) tr[x].l
		#define r(x) tr[x].r
		#define val(x) tr[x].val
		#define mx(x) tr[x].maxv
	} tr[N << 2];

	void build(int l, int r, int x) {
		tr[x] = {l, r};
		if (l == r) return;
		int mid = (l + r) / 2;
		build(l, mid, x * 2), build(mid + 1, r, x * 2 + 1);
	}

	int solve(int x, double lim) {
		if (mx(x) <= lim) return 0;
		if (l(x) == r(x)) return 1;
		if (mx(x * 2) <= lim) return solve(x * 2 + 1, lim);
		else return solve(x * 2, lim) + val(x) - val(x * 2);
	}

	void pushup(int x) {
		mx(x) = max(mx(x * 2), mx(x * 2 + 1));
		val(x) = val(x * 2) + solve(x * 2 + 1, mx(x * 2));
	}

	void update(int p, int x, double v) {
		if (l(x) == r(x)) {
			mx(x) = v;
			val(x) = 1;
			return;
		}
		int mid = (l(x) + r(x)) / 2;
		if (p <= mid) update(p, x * 2, v);
		else update(p, x * 2 + 1, v);
		pushup(x);
	}

} SegT;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr); cout.tie(nullptr);

    cin >> n >> m;
    SegT.build(1, n, 1);

    for (int i = 1; i <= m; i ++) {
    	int x, y;
    	cin >> x >> y;
    	SegT.update(x, 1, (double)y / x);
    	cout << SegT.val(1) << '\n';
    }

    return 0;
}
posted @ 2024-02-07 10:51  Svemit  阅读(50)  评论(0)    收藏  举报