[BZOJ29957] 楼房重建 - 线段树

2957: 楼房重建

Time Limit: 10 Sec  Memory Limit: 256 MB
Submit: 3294  Solved: 1554
[Submit][Status][Discuss]

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
数据约定
  对于所有的数据1<=Xi<=N,1<=Yi<=10^9
N,M<=100000
 

 
题解:
那线段树维护区间的最大斜率和斜率最长上升子序列的长度;
甚至建树都不需要;
唯一的问题就是update,我们好像不可以O(1)合并。
我们把一个序列分为左右两端记为L, R;
如果R的最大斜率小于L的最大斜率,那么有区间直接就不用考虑了;
如果R的最大斜率大于L的最大斜率,那么我们二分R区间,像这样递归下去;
 

 
Code:
#include <iostream>
#include <cstdio>
using namespace std;

struct segment
{
    int len;
    long double mx;
}t[400010];
#define ls(o) o<<1
#define rs(o) o<<1|1
#define mx(o) t[o].mx
#define len(o) t[o].len

inline int count(int l, int r, int o, long double hi)
{
    if (l == r) return mx(o) > hi;
    int mid = l + r >> 1;
    if (mx(ls(o)) <= hi) return count(mid + 1, r, rs(o), hi);
    return len(o) - len(ls(o)) + count(l, mid, ls(o), hi);
}

inline void change(int l, int r, int o, int to, long double k)
{
    if (l == r) 
    {
        len(o) = 1;
        mx(o) = k;
        return ;
    }
    int mid = l + r >> 1;
    if (to <= mid) change(l, mid, ls(o), to, k);
    else change(mid + 1, r, rs(o), to, k);
    mx(o) = max(mx(ls(o)), mx(rs(o)));
    len(o) = len(ls(o)) + count(mid + 1, r, rs(o), mx(ls(o)));
}

int main()
{    
    int n, m;
    scanf("%d%d", &n, &m);
    while (m--)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        change(1, n, 1, x, (long double)y / x);
        printf("%d\n", len(1));
    }
    return 0;
}

 

posted @ 2018-07-03 18:34  zZhBr  阅读(178)  评论(0编辑  收藏  举报