您的位置:首页 > 其它

【bzoj2957】楼房重建 分块+二分查找

2017-04-24 12:49 169 查看
题目描述

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

输入

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

输出

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

样例输入

3 4

2 4

3 6

1 1000000000

1 1

样例输出

1

1

1

2

题解

线段树 分块+二分查找

题目大意说白了就是给定n个斜率,求这些斜率的连续递增数列(从1开始,每有一个比前一个大的就选定)的长度,多次修改。

看了下数据范围可以分块求。

先暴力搞定每个块的递增数列,把这些斜率从小到大塞到一个栈里边(时间复杂度O(n/b),b为块的大小)。

然后查找时从头开始,在每个块对应的栈中二分查找第一个斜率比前一个大的位置,这个位置和栈里面后边的位置都能被看到(时间复杂度O(blog(n/b)))。

总时间复杂度为O(n*(n/b + blog(n/b)))≈O(n*(n/b + blogn))。

这样一来b=√(n/logn)比较合算,但其实也没什么卵用,直接√n一块就行。

#include <cstdio>
#include <cmath>
#include <algorithm>
#define N 100010
using namespace std;
int n , si , h
;
struct data
{
int top , sta[400];
}a[400];
bool cmp(int a , int b)
{
if(!b) return h[a] > 0;
return (long long)h[a] * b > (long long)h[b] * a;
}
int main()
{
int m , i , si , p , l , r , mid , ans , tmp , last;
scanf("%d%d" , &n , &m) , n ++ ;
si = (int)sqrt(n);
while(m -- )
{
scanf("%d" , &p);
scanf("%d" , &h[p]);
l = p / si * si , r = min(n , (p / si + 1) * si);
a[p / si].top = 0;
for(i = l ; i < r ; i ++ )
if(cmp(i , a[p / si].sta[a[p / si].top]))
a[p / si].sta[++a[p / si].top] = i;
ans = 0 , last = 0;
for(i = 0 ; i <= (n - 1) / si ; i ++ )
{
l = 1 , r = a[i].top , tmp = 0;
while(l <= r)
{
mid = (l + r) >> 1;
if(cmp(a[i].sta[mid] , last)) tmp = mid , r = mid - 1;
else l = mid + 1;
}
if(tmp) last = a[i].sta[a[i].top] , ans += a[i].top - tmp + 1;
}
printf("%d\n" , ans);
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: