• [题解] 楼房重建 (LG3800)


    题意

    一个平面上有 (n) 个点,(x)([1, n]) 之间的整数。

    每次修改一个点的 (y),求平面的上凸壳大小,即斜率的单调栈大小。

    做法

    首先想到直接单调栈维护,因为有修改,可能用到线段树等支持区间合并的数据结构。

    但单调栈不能快速合并,显然不能做。

    考虑每次合并左右两个区间,左边的最大值会对右边的长度造成影响,所以在每个点维护区间 max,单调栈的长度 (len)

    每次合并时,左边的区间不用计算,可以直接加上。右边可以递归处理:

    设左边的最大斜率 (k)

    • (max l le k),则跳过左边,在右边递归处理。
    • (max l > k),则在左边递归处理,显然 (k) 对右边没有影响,直接加上即可。

    怎么维护右边受到左边限制的凸壳大小?因为 (x) 点的凸壳是左边和右边(受左边限制)的并,求 (len_x - len_l) 即可。

    代码

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    const int N = 200005;
    const double eps = 1e-10;
    
    double mx[N * 4];
    int len[N * 4];
    
    int getlen(int x, int l, int r, double k){
      if(l == r) return mx[x] - k > eps;
      int mid = (l + r) >> 1;
      if(mx[x << 1] - k > eps) return getlen(x << 1, l, mid, k) + len[x] - len[x << 1];
      else return getlen(x << 1 | 1, mid + 1, r, k);
    }
    void modify(int x, int l, int r, int p, double v){
      if(l == r){
        mx[x] = v; len[x] = 1;
        return;
      }
      int mid = (l + r) >> 1;
      p <= mid ? modify(x << 1, l, mid, p, v) : modify(x << 1 | 1, mid + 1, r, p, v);
      mx[x] = max(mx[x << 1], mx[x << 1 | 1]);
      len[x] = len[x << 1] + getlen(x << 1 | 1, mid + 1, r, mx[x << 1]);
    }
    
    int main(){
      int n, m;
      scanf("%d%d", &n, &m);
      for(int i=1; i<=m; i++){
        int p, h;
        scanf("%d%d", &p, &h);
        modify(1, 1, n, p, 1. * h / p);
        printf("%d
    ", len[1]);
      }
      return 0;
    }
    
  • 相关阅读:
    关于docker remote api未授权访问漏洞的学习与研究
    c#调用 WinRAR.exe以命令行形式实现文件、文件夹的解压缩
    Redis作为多个Windows服务运行配置方法
    全景播放器-js+flash
    SQL 拼接多个字段的值&一个字段多条记录的拼接
    mysql-connector-java与mysql版本的对应
    Sass map详解
    情人节网站logo赏析
    表单标签动画畅想
    段落文字彩条效果
  • 原文地址:https://www.cnblogs.com/RiverHamster/p/sol-lg3800.html
Copyright © 2020-2023  润新知