BZOJ 2957 楼房重建

Description

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

Input

第一行两个正整数\(N,M\)
接下来\(M\)行,每行两个正整数\(X_{i},Y_{i}\)

Output

M行,第i行一个整数表示第\(i\)天过后小A能看到的楼房有多少栋。

Sample Input

3 4
2 4
3 6
1 1000000000
1 1

Sample Output

1
1
1
2

Hint

数据约定

对于所有的数据\(1 \le X_{i} \le N\)\(1 \le Y_{i} \le 10^{9}\)
\(N,M \le 100000\)

很早就听说过这一题的大名,却一直没有去写,今天来写一下。
我们用线段树来维护斜率的一条上升的链,\(L_{i}\)表示链左端的斜率,\(R_{i}\)表示链右端的斜率。
考虑如何维护,对于区间\(l \sim r\)一段区间,我们用\(l \sim mid\)这段的右端点,放到\(mid+1 \sim r\)中询问,复杂度\(O(nlog^{2}n)\)

#include<cmath>
#include<cstdio>
#include<cstdlib>
using namespace std;

#define eps (1e-12)
#define maxn 400010
int n,m,len[maxn]; double L[maxn],R[maxn];

inline bool equal(double a,double b) { return fabs(a-b) < eps; }
inline bool dy(double a,double b) { if (equal(a,b)) return false; return a > b; }
inline bool xd(double a,double b) { if (equal(a,b)) return true; return a <= b; }

inline void query(int l,int r,int now,int &k1,double &k2)
{
	if (dy(L[now],k2)) k1 += len[now],k2 = R[now];
	if (l == r) return;
	int mid = (l + r) >> 1;
	if (xd(R[now<<1],k2)) query(mid+1,r,now<<1|1,k1,k2);
	else
	{
		query(l,mid,now<<1,k1,k2);
		k1 += len[now] - len[now<<1]; k2 = R[now];
	}
}
inline void update(int l,int r,int now)
{
	len[now] = len[now<<1],L[now] = L[now<<1],R[now] = R[now<<1];
	int mid = (l + r) >> 1;
	query(mid+1,r,now<<1|1,len[now],R[now]);
}
inline void change(int l,int r,int x,int y,int now)
{
	if (l == r) { L[now] = R[now] = 1.0*y/x; len[now] = (y > 0); return; }
	int mid = (l + r) >> 1;
	if (x <= mid) change(l,mid,x,y,now<<1);
	else change(mid+1,r,x,y,now<<1|1);
	update(l,r,now);
}

int main()
{
	freopen("2957.in","r",stdin);
	freopen("2957.out","w",stdout);
	scanf("%d %d",&n,&m);
	while (m--)
	{
		int x,y; scanf("%d %d",&x,&y);
		change(1,n,x,y,1);
		printf("%d\n",len[1]);
	}
	fclose(stdin); fclose(stdout);
	return 0;
}
posted @ 2015-04-14 14:07  lmxyy  阅读(121)  评论(0编辑  收藏  举报