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;
}
$\color{blue} \mathcal {Lordreamland}$

浙公网安备 33010602011771号