您的位置:首页 > 其它

【bzoj2957】楼房重建 (线段树)

2017-07-06 16:38 218 查看

Description

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

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

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

Input

第一行两个正整数N,M

接下来M行,每行两个正整数Xi,Yi

Output

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

Sample Input

3 4
2 4
3 6
1 1000000000
1 1


Sample Output

1
1
1
2


data

对于所有的数据1<=Xi<=N,1<=Yi<=109,N,M<=105

I think

对于每一个楼房的位置(x,y),用斜率y/x替代它的高度,那么一栋楼房能被看到当且仅当其高度大于之前所有楼房的高度。

因此我们用线段树来维护斜率。数组mx[]表示该区间的最大高度,数组t[]表示该区间可见楼房数。在区间合并更新t[i]时,t[i<<1]一定包含,而对于右区间,将其分为u,v左右两个区间。若mx[u]<=mx[i<<1],直接递归右区间v,否则返回t[i<<1|1]-t[i<<1] ( 表示v区间可见楼房数,由层层递归上来的性质,v区间可见楼房的高度一定大于mx[u],则必然大于mx[i<<1],注意这里不能直接调用t[i<<1|1] )与递归u答案之和。

Code

#include<cstdio>
#include<algorithm>
using namespace std;
const int sm = 1e5+50;

int n,m,x,y,t[sm<<2];
double mx[sm<<2];
char ch;
void read(int &x) {
x=0,ch=getchar();
while(ch>'9'||ch<'0')ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
}

int cal(int i,int l,int r,double val) {
if(l==r)return mx[i]>val;
int m=(l+r)>>1;
if(mx[i<<1]<=val) return cal(i<<1|1,m+1,r,val);
return t[i]-t[i<<1]+cal(i<<1,l,m,val);
}

void update(int i,int l,int r,int loc,double k) {
if(l==r) { mx[i]=k,t[i]=1;return; }
int m=(l+r)>>1;
if(loc<=m) update(i<<1,l,m,loc,k);
else update(i<<1|1,m+1,r,loc,k);
mx[i]=max(mx[i<<1],mx[i<<1|1]);
t[i]=t[i<<1]+cal(i<<1|1,m+1,r,mx[i<<1]);
}

int main() {
read(n),read(m);
for(int i=1;i<=m;++i) {
read(x),read(y);
update(1,1,n,x,(double)1.0*y/x);
printf("%d\n",t[1]);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: