楼房重建(线段树)

楼房重建

题目描述

小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数数每天在建筑队完工之后,他能看到多少栋楼房?

输入输出格式

输入格式:

第一行两个正整数\(N,M\)

接下来\(M\)行,每行两个正整数\(X_i,Y_i\)

输出格式:

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

输入输出样例

输入样例#1:

3 4
2 4
3 6
1 1000000000
1 1

输出样例#1:

1
1
1
2

说明

对于所有的数据\(1\leq X_i\leq N\)\(1\leq Y_i\leq 10^9\)

\(N,M\leq100000\)

诡异的线段树?
暴力很好想,统计出前面所有斜率的最大值判断当前点的斜率是否大于这个最大值。
如何用线段树进行维护?
由于这个最大值是会随着区间后移变化的,所以线段树中左子树的最大值是会影响右子树的贡献,这导致我们不容易合并两个子树的答案。
考虑线段树内一个值发生变化,左子树对右子树的影响。
如果左子树的最大值比之前的最大值大,那么右子树受到的影响不会改变,直接递归左子树算答案即可(右子树对答案的贡献和上一次一样)。
如果左子树的最大值比之前的最大值小,那么左子树在当前状态下必定不能对答案造成贡献,直接递归右子树即可。
时间复杂度:\(O(能过)\)

#include<bits/stdc++.h>
#define ll(x) (x<<1)
#define rr(x) (x<<1|1)
using namespace std;
int read(){
	int x=0,w=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')w=-1;ch=getchar();}
	while(ch>='0'&&ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return x*w;
}
const int N=100010;
int n,m,x,y,ans;
int sgm[4*N];
double maxl[4*N],minl[4*N];
double query(int root,int l,int r,double v){
	if(l>r)return 0;
	if(l==r)return maxl[root]>v;
	if(minl[root]>v)return sgm[root];
	int mid=(l+r)/2;
	if(v>=maxl[ll(root)]) return query(rr(root),mid+1,r,v);
	else return query(ll(root),l,mid,v)+sgm[root]-sgm[ll(root)];
}
void push_up(int x,int l,int r){
	sgm[x]=sgm[ll(x)];
	sgm[x]+=query(rr(x),l,r,maxl[ll(x)]);
	maxl[x]=max(maxl[ll(x)],maxl[rr(x)]);
	minl[x]=min(minl[ll(x)],minl[rr(x)]);
}
void insert(int root,int l,int r,int p,double v){
	if(l>r)return;
	if(l==r&&l==p){sgm[root]=1;maxl[root]=minl[root]=v;return;}
	int mid=(l+r)/2;
	if(p<=mid) insert(ll(root),l,mid,p,v);
	else insert(rr(root),mid+1,r,p,v);
	push_up(root,mid+1,r);
}
int main(){
	n=read();m=read();
	memset(maxl,-127,sizeof(maxl));memset(minl,127,sizeof(minl));
	for(int i=1;i<=m;i++){
		x=read();y=read();insert(1,1,n,x,1.0*y/x);
		printf("%d\n",sgm[1]);
	}return 0;
}
posted @ 2018-10-06 10:49  Frozen_Heart  阅读(128)  评论(0编辑  收藏  举报