P4198 楼房重建

P4198 楼房重建

题意:

给你n个点每个点都有一座楼房,小明站在(0,0)点位置看楼房,如果这栋楼房上任何一个高度大于 0 的点与 (0,0)的连线没有与之前的线段相交,那么这栋楼房就被认为是可见的

现在你有q次操作, 每次可以改变任意楼房的高度。问每次改变时小明最多能看多少楼房?

题解:

这题不难想到小明最多可以看都楼房和斜率有关, 也就是从第一栋楼开始 斜率一直递增的小明都能看得到。

所以现在问题就转化为:从第一个点开始找一个严格递增的子序列且最长。

怎么解决这个问题呢?

考虑到有单点修改操作,可以用线段树去维护, 线段树怎么维护呢?

线段树维护一个最大值, 和一个len表示 从当前节点左左边为基础到右边,找一个长度最长且严格递增的子序列的长度。

维护最大众不用多少很好维护。

那如何维护len呢?

首先 每个节点的len 一定等于左儿子的len + 上右儿子的一部分, 怎么找到右儿子的长度呢?

我们已经知道了右儿子的最大值是多少了, 如果找到左儿子中第一个大于右儿子的最大值的。

且知道了以第一个大于右儿子最大值一直严格单调递减的长度,是不是就知道,左儿子的贡献了。

具体看代码理解把。

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 7;

#define m (l + r) / 2
#define lson 2 * node
#define rson 2 * node + 1

struct segment{
    int len;
    double maxn;
}tree[4 * N];

int work(double k, int l, int r, int node) {

    if (l == r) {
        return 0;
    }
    if (tree[lson].maxn > k) {
        return work(k, l, m, lson);
    } else {
        if (tree[rson].len == tree[node].len) {
            return work(k, m + 1, r, rson);
        }
        return work(k, m + 1, r, rson) + tree[node].len - tree[rson].len;
    }

}

void update(int pos, double v, int l, int r, int node) {
    if (l == r) {
        tree[node].len = 1;
        tree[node].maxn = v;
        return;
    }
    if (pos <= m) update(pos, v, l, m, lson);
    else update(pos, v, m + 1, r, rson);
    tree[node].maxn = max(tree[lson].maxn, tree[rson].maxn);
    if (tree[rson].maxn <= tree[lson].maxn) {
        tree[node].len = tree[lson].len;
    } else {
        tree[node].len = tree[lson].len + tree[rson].len  - work(tree[lson].maxn, m + 1, r, rson);
    }
   
}


int main() { 
    int n, q;
    scanf("%d %d", &n, &q);
    while (q--) {
        int x, y;
        scanf("%d %d", &x, &y);
        update(x, (double)y / (double)x, 1, n, 1);
        printf("%d\n", tree[1].len);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值