LuoguP4198 楼房重建

根据生活实际,一个楼房要想被看到,他的最高点与原点的连线不能经过其他楼房。
如果我们把一个楼房转化成他的最高点与原点连线的斜率,那么就是要维护一个斜率单调递增的序列。
来一棵线段树,每个节点维护区间斜率最大值和区间内的答案(以区间左端点为原点)。
左右区间如何合并?
左区间的答案肯定直接加上,然后再加上右区间里斜率大于左区间最大值且能被看到的点的数量。
如何求一个节点的区间内大于某个值\(k\)且能被看到的点的数量?
如果左区间最大值不大于\(k\),直接递归右区间。
否则递归左区间,并累加当前区间的答案减去左区间的答案,这个答案是题目要求的答案,相当于累加了右区间能被看到的点数,右区间里符合要求的一定就是这些点。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define df long double
#define pp pop_back
#define pb push_back
#define ins insert
#define lowbit(x) x & -x

const int N = 1e5 + 4;
const int mod = 1e9 + 7;
const ll INF = 2e18;
const df eps = 1e-10;

int read(){int x; scanf("%d", &x); return x; }
ll readll(){ll x; scanf("%lld", &x); return x; }

int n, m;
struct sgt{
	int l, r;
	int res;
	df mx;
}t[N * 4];

void build(int p, int l, int r){
	t[p].l = l;
	t[p].r = r;
	t[p].res = t[p].mx = 0;
	if(l == r) return;
	int mid = (l + r) >> 1;
	build(p * 2, l, mid);
	build(p * 2 + 1, mid + 1, r);
}

int ask(int p, df x){
	int l = t[p].l, r = t[p].r;
	if(t[p].mx <= x) return 0;
	if(l == r) return t[p].mx > x;
	int mid = (l + r) >> 1;
	if(t[p * 2].mx <= x) return ask(p * 2 + 1, x);
	else return ask(p * 2, x) + t[p].res - t[p * 2].res;
}

void chg(int p, int x, df k){
	int l = t[p].l, r = t[p].r;
	if(l == r){
		t[p].res = 1, t[p].mx = k;
		return;
	}
	int mid = (l + r) >> 1;
	if(x <= mid) chg(p * 2, x, k);
	else chg(p * 2 + 1, x, k);
	t[p].mx = max(t[p * 2].mx, t[p * 2 + 1].mx);
	t[p].res = t[p * 2].res + ask(p * 2 + 1, t[p * 2].mx);
}

signed main(){
	n = read(), m = read();
	build(1, 1, n);
	for(int i = 1; i <= m; i++){
		int x = read(), y = read();
		chg(1, x, (df)(1.0 * y / x));
		printf("%d\n", t[1].res);
	}
	return 0;
}

posted @ 2025-08-10 17:30  Lordreamland  阅读(6)  评论(0)    收藏  举报