P4198 楼房重建

P4198 楼房重建

一道优秀线段树题

题目大意,有1至n个位置,每个位置有一个楼房,初始高度为0,高度为\(h_i\)的楼房可以抽象成端点为\((i,0),(i,h_i)\)的线段

你站在\((0,0)\),看到一栋楼的前提是\((0,0)\)\((i,h_i)\)的连线不与任何线段相交

每次修改一栋楼的高度,问你能看到的楼房有多少

首先考虑求出\((0,0)\)\((i,h_i)\)的连线的斜率\(k_i\),题目求满足\(\forall_{j<i}k_j<k_i\)的i的个数,考虑线段树维护区间\([l,r]\)的最高点,和其\(\forall_{l\le j<i,l\le i\le r}k_j<k_i\)的个数

然后发现对于两个区间合并,对于题意,左区间一定会选,所以就维护一个询问,在一个区间,有最小高度限定的

\(\forall_{l\le j<i,l\le i\le r}k_j<k_i\&\& \lim<k_i\)的个数

然后发现对于左区间,若最大值大于lim,则右区间一定满足,只用递归左区间

如果最大值小于等于lim,则左区间无贡献,只用递归右区间

然后就可以\(O(n\log^2n)\)解决本题

#include<bits/stdc++.h>
using namespace std;
int n,m,a[100005];
struct node{
	int l;
	double h;
}t[100005<<2];
int ask(int o,int l,int r,double lim){
	if(l==r){
		if(t[o].h>lim) return 1;
		else return 0;
	}int mid=l+r>>1;
	if(t[o].h<=lim) return 0;
	if(t[o*2].h>lim) return t[o].l-t[o*2].l+ask(o*2,l,mid,lim);
	else return ask(o*2+1,mid+1,r,lim);
}
void change(int o,int l,int r,int x,double height){
	if(l==r){
		t[o]={1,height};
		return ;
	}int mid=l+r>>1;
	if(mid>=x) change(o*2,l,mid,x,height);
	else change(o*2+1,mid+1,r,x,height);
	t[o].h=max(t[o*2].h,t[o*2+1].h),t[o].l=t[o*2].l+ask(o*2+1,mid+1,r,t[o*2].h);
}
int main(){
	scanf("%d%d",&n,&m);
	while(m--){
		int w,h;
		scanf("%d%d",&w,&h);
		change(1,1,n,w,1.0*h/w);
		printf("%d\n",t[1].l);
	} 
	return 0;
}
posted @ 2024-02-22 20:50  zhy_learn  阅读(6)  评论(0编辑  收藏  举报