bzoj 2957 楼房重建

  小A的楼房外有一大片施工工地,工地上有N栋待建的楼房。每天,这片工地上的房子拆了又建、建了又拆。他经常无聊地看着窗外发呆,数自己能够看到多少栋房子。 
  为了简化问题,我们考虑这些事件发生在一个二维平面上。小A在平面上(0,0)点的位置,第i栋楼房可以用一条连接(i,0)和(i,Hi)的线段表示,其中Hi为第i栋楼房的高度。如果这栋楼房上任何一个高度大于0的点与(0,0)的连线没有与之前的线段相交,那么这栋楼房就被认为是可见的。 
  施工队的建造总共进行了M天。初始时,所有楼房都还没有开始建造,它们的高度均为0。在第i天,建筑队将会将横坐标为Xi的房屋的高度变为Yi(高度可以比原来大—修建,也可以比原来小—拆除,甚至可以保持不变—建筑队这天什么事也没做)。请你帮小A数数每天在建筑队完工之后,他能看到多少栋楼房?

思路:

线段树,点修改。 求递增的数有多少个。 满足后一个点的斜率大于前一个点的斜率。

我们线段树维护两个值。最大的斜率,能看到的楼房的个数,就是递增的值。

我们修改点,然后更新父亲节点的时候,我们要注意一下,

左儿子可以直接加上。然后右区间的楼房可能被左区间的楼房遮住。

判断右区间。 设左儿子的最大斜率是 M、 右区间的左儿子的最大斜率为 MM。

如果 M > MM 。 说明右区间的左儿子全部被遮住。就一个也看不到。所以我们在找右区间的右儿子。

如果 M< MM. 这就说明我们知道了右区间的右儿子的值了, 就是 右区间的值 - 右区间的左儿子的值。

然后我们在找右区间的左儿子的值。

 

#include <bits/stdc++.h>
#define mem(x,v) memset(x,v,sizeof(x)) 
#define MID(x,y) ((x + y) >> 1)
#define go(i,a,b)  for (int i = a; i <= b; i++)
#define og(i,a,b)  for (int i = a; i >= b; i--)
using namespace std;
typedef long long LL;
const double EPS = 1e-10;
const int INF = 0x3f3f3f3f;
const int N = 1e5+1000;
int val[N*4];
double rake[N*4];
int n,m;
void Build(int now, int a, int b){
	val[now] = 0; rake[now] = 0;
	if (a + 1 == b) return;
	int m = MID(a,b);
	Build(now<<1,a,m);
	Build(now<<1|1,m,b);
}
int Query(int now, int a, int b, double k){
	if (a+1 == b) return k < rake[now];
	int m = MID(a,b);
	if (k >= rake[now*2]) return Query(now*2+1,m,b,k);
	return val[now] - val[now*2] + Query(now*2,a,m,k);
}
void Insert(int now, int a,int b,int l, double k){
	if (a + 1 == b) {
		val[now] = 1; rake[now] = k; return;
	}
	int m = MID(a,b);
	if (l < m) Insert(now*2,a,m,l,k);
	if (l >= m) Insert(now*2+1,m,b,l,k);
	rake[now] = max(rake[now*2],rake[now*2+1]);
	val[now] = val[now*2] + Query(now*2+1,m,b,rake[now*2]);
}
int main(){	
	scanf("%d%d",&n,&m);
	Build(1,1,n+1);
	go(i,1,m){
		int x,y;
		scanf("%d%d",&x,&y);
		Insert(1,1,n+1,x,y*1.0/x);
		printf("%d\n",val[1]);
	}
	return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值