P4198 楼房重建

P4198 楼房重建

一道优秀线段树题

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

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

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

首先考虑求出(0,0)(i,hi)的连线的斜率ki,题目求满足j<ikj<ki的i的个数,考虑线段树维护区间[l,r]的最高点,和其lj<i,lirkj<ki的个数

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

lj<i,lirkj<ki&&lim<ki的个数

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

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

然后就可以O(nlog2n)解决本题

#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 @   zhy_learn  阅读(8)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示