欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Luogu P4198 / bzoj 2957 楼房重建 线段树

程序员文章站 2022-07-13 11:58:15
...

题目:  luogu4198          bzoj2957

小A的楼房外有一大片施工工地,工地上有N栋待建的楼房。每天,这片工地上的房子拆了又建、建了又拆。他经常无聊地看着窗外发呆,数自己能够看到多少栋房子。

为了简化问题,我们考虑这些事件发生在一个二维平面上。小A在平面上(0,0)点的位置,第i栋楼房可以用一条连接(i,0)和(i,Hi)的线段表示,其中Hi为第i栋楼房的高度。如果这栋楼房最高点与(0,0)的连线没有与之前的线段相交,那么这栋楼房就被认为是可见的。

施工队的建造总共进行了M天。初始时,所有楼房都还没有开始建造,它们的高度均为0。在第i天,建筑队将会将横坐标为Xi的房屋的高度变为Yi(高度可以比原来大—修建,也可以比原来小—拆除,甚至可以保持不变—建筑队这天什么事也没做)。请你帮小A数数每天在建筑队完工之后,他能看到多少栋楼房?

分析:

建楼的图大概这样……瞎画的

Luogu P4198 / bzoj 2957 楼房重建 线段树

可以发现,斜率是该楼是否可见的关键。(好像是废话)(以下的“大”、“小”都指斜率)

现在可以大体想出做法:

每更改一次,看更改处的左边是否有比它大的,有的话此次更改对答案无贡献;没有的话再看右边,右边比它大的才可见。

具体要用到线段树优化。

维护区间最大值和区间上升序列长度即可。
#include<bits/stdc++.h>	
using namespace std;
int n,m;
double a[100010];
struct building{
	double mx;//区间最大值 
	int len;//区间上升序列长度 
}t[400010];
void pushup1(int x)
{
	t[x].mx=max(t[x<<1].mx ,t[x<<1|1].mx);//维护最大值 
}
int pushup2(double mxx,int x,int l,int r)//关键部分 
{
	if(t[x].mx<=mxx)	return 0;//对答案无贡献 
	if(a[l]>mxx)		return t[x].len; 
	if(l==r)	return a[l]>mxx;//若>,返回1;若<=,返回0 
	int s1=x<<1,s2=x<<1|1;//两个子区间 
	int mid=(l+r)>>1;
	if(t[s1].mx<=mxx)	return pushup2(mxx,s2,mid+1,r);
	else	return pushup2(mxx,s1,l,mid)+t[x].len-t[s1].len;
} 
void change(int x,int l,int r,int a,int b)
{
	if(l==r && l==a)
	{
		t[x].mx=(double)b/a;//斜率 
		t[x].len=1;
		return;
	}
	int mid=(l+r)>>1;
	if(a<=mid)	change(x<<1,l,mid,a,b);
	else 	change(x<<1|1,mid+1,r,a,b);//递归 
	pushup1(x);//维护区间最值 
	t[x].len=t[x<<1].len+pushup2(t[x<<1].mx,x<<1|1,mid+1,r);//维护区间上升序列长度 
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		a[x]=(double)y/x;//斜率 
		change(1,1,n,x,y);//更改 
		printf("%d\n",t[1].len);
	}
	return 0;
} 

相关标签: 线段树