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;
}
标签:return,int,楼房,mid,lim,P4198,区间,重建
From: https://www.cnblogs.com/zhy114514/p/18028160