楼房重建

https://www.luogu.org/blog/violet2333/p4198-lou-fang-zhong-jian

题目描述

小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能看到的楼房有多少栋

输入输出样例

输入 #1

3 4

2 4

3 6

1 1000000000

1 1

输出 #1

1

1

1

2

说明/提示

对于所有的数据1<=Xi<=N,1<=Yi<=10^9

N,M<=100000


emmm~~

经过艰辛万苦,终于弄懂题解啦做出来了。

当然,也有dalao的指导~

线段树维护 序列中的最大斜率,序列长度(即能够看见的楼房个数);

pushup1:更新区间最大的斜率;

pushup2:更新区间的序列长度;

  • 修改后

    更新每一个区间的最大斜率和长度(划重点);

  • pushup2

    ①左儿子最大斜率≥右儿子最大斜率:

    右儿子全部舍掉

    ②左儿子最大斜率<右儿子的左端点的斜率:

    ∴左儿子对右儿子没有影响,直接返回右儿子的序列长度即可

    ③右儿子只有一个元素: 还用教吗?

    ④不满足以上,左儿子最大斜率<右儿子最大斜率:

    递归右儿子。
    简称 右儿子的左儿子 为 右左儿子,
         右儿子的右儿子 为 右右儿子。
    如果右左儿子最大斜率≤左儿子的最大斜率。只递归右右儿子即可。
    否则递归右左儿子的序列长度+右儿子序列长度-右左儿子的序列长度。

代码:

#include 
using namespace std;

const int N = 100005;
struct node {
    double mx;
    int len;  //序列中的最大斜率,序列长度
} t[N << 2];
int n, m;
double a[N];  // i点的斜率

void pushup1(int u) { //更新大区间的最大斜率
    t[u].mx = max(t[u << 1].mx, t[u << 1 | 1].mx); 
}

int pushup2(double lmx, int u, int l, int r) {
    if (t[u].mx <= lmx)
        return 0;  //如果左儿子最大斜率>右儿子最大斜率,右边全部舍掉
    if (a[l] > lmx)
        return t[u].len;  //如果右儿子中左端点斜率>左儿子最大斜率,返回右儿子区间长度即可
    if (l == r)
        return a[l] > lmx;  //如果只有一个元素,判断该元素斜率与左儿子最大斜率的大小,返回1/0即可;
    int mid = (l + r) >> 1;            //拆分右儿子,继续递归
    int s1 = u << 1, s2 = u << 1 | 1;  //(大区间右儿子的)左儿子,右儿子
    if (t[s1].mx <= lmx)
        return pushup2(lmx, s2, mid + 1, r);
    else
        return pushup2(lmx, s1, l, mid) + t[u].len - t[s1].len;
}

void change(int u, int l, int r, int x, int y) {
    if (l == r && l == x) {  //序列中只有一个元素
        t[u].mx = (double)y / x;
        t[u].len = 1;
        return;
    }
    int mid = l + r >> 1;
    if (x <= mid)
        change(u << 1, l, mid, x, y);
    if (x > mid)
        change(u << 1 | 1, mid + 1, r, x, y);
    pushup1(u);
    t[u].len = t[u << 1].len + pushup2(t[u << 1].mx, u << 1 | 1, mid + 1, r);  //更新大区间的序列长度
    /*左儿子序列一定都在大区间中;
    ∵右儿子会受到左儿子的影响
    ∴递归右儿子,更新右儿子中可以看到的数量,加入大区间长度中
    */
}

int main() {
    scanf("%d%d", &n, &m);
    while (m--) {
        int x, y;
        scanf("%d%d", &x, &y);
        a[x] = (double)y / x;      //存入斜率
        change(1, 1, n, x, y);     //修改
        printf("%d\n", t[1].len);  //输出序列长度
    }
}

你可能感兴趣的:(楼房重建)