LG 题解 P4198 楼房重建

前置芝士

  • 线段树

Description

一共有 \(n\) 栋楼,小明坐在 \((0,0)\),第 \(i\) 栋楼房用 \((i,h_i)\) 表示。有 \(m\) 次修改,每次修改一栋楼的高度,可能变高或边矮,修改后输出小明能看到楼的数量。

Solution

稍微分析一下就能得出我们要求一个最长上升的斜率。

考虑如何维护这个看起来不可维护东西。

下面的表示方式可能有所不适,但这是我能想到的一种最清晰的表达方式了

我们设 \(mx[l,r]\) 表示 \([l,r]\) 区间斜率的最大值,\(len[l,r]\) 表示 \([l,r]\) 区间最长合法长度,\(k_{i}\) 表示第 \(i\) 栋楼的斜率。

假设当前我们要更新区间 \([l,r]\),更小的区间已经被更新了。

\(mid\) 为分解点,\(midr\) 为区间 \((mid,r]\) 的分解点,\(lm = mx[l,mid]\)

显然左区间的 \(len[l,mid]\) 每次都要选,我们用递归函数 \(Queryr(mid+1,r)\) 表示递归去处理 \((mid,r]\) 区间(右区间)的结果。

递归时:

  • 如果 \(lm \ge mx(mid, midr]\),那么返回 \(Queryr(midr+1,r)\)。这个右区间的左区间已经不能选了。
  • 否则,返回 \(Queryr(mid+1,midr) + len(mid,r] - len(midr,r]\)。右区间的左区间的最大值比 \(lm\) 还大,说明右区间的右区间都能选上,但右区间的左区间不确定,需要递归求解。

下面是几个结束条件:

  • 如果 \(lm > mx(mid,r]\),那么 \(len[l,r] = len[l,mid]\)。考虑实际意义,左边的楼房把右边的全挡住了,右边的就都看不到了。
  • 如果 \(lm < k_{mid+1}\),那么 \(len[l,r] = len[l,mid] + len(mid,r]\)。在右区间中,如果第一栋楼都能被看见,那么右区间能看到的楼在 \(l\) 处都能看见。
  • 如果右区间长度为 \(1\),直接判断 \(lm\)\(k_r\) 的关系即可。

实现细节看代码吧。

Code

/*
Work by: Suzt_ilymics
Problem: 不知名屑题
Knowledge: 垃圾算法
Time: O(能过)
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#define LL long long
#define orz cout<<"lkp AK IOI!"<<endl

using namespace std;
const int MAXN = 2e5+5;
const int INF = 1e9+7;
const int mod = 1e9+7;

int n, m;
double a[MAXN];

int read(){
    int s = 0, f = 0;
    char ch = getchar();
    while(!isdigit(ch))  f |= (ch == '-'), ch = getchar();
    while(isdigit(ch)) s = (s << 1) + (s << 3) + ch - '0' , ch = getchar();
    return f ? -s : s;
}

namespace Seg {
    #define lson i << 1
    #define rson i << 1 | 1
    struct Tree {
        int len; double max;
    }tree[MAXN << 2];
    void Push_up(int i) { tree[i].max = max(tree[lson].max, tree[rson].max); }
    int Queryr(int i, int l, int r, double lmax) {
        if(tree[i].max <= lmax) return 0;
        if(a[l] > lmax) return tree[i].len;
        if(l == r) return a[l] > lmax;
        int mid = (l + r) >> 1;
        if(tree[lson].max <= lmax) return Queryr(rson, mid + 1, r, lmax);
        else return Queryr(lson, l, mid, lmax) + (tree[i].len - tree[lson].len);
    }
    void Modify(int i, int l, int r, int L, int R, int val) {
        if(L <= l && r <= R) {
            tree[i].max = (1.0 * val / L);
            tree[i].len = 1;
            return ;
        }
        int mid = (l + r) >> 1;
        if(mid >= L) Modify(lson, l, mid, L, R, val);
        if(mid < R) Modify(rson, mid + 1, r, L, R, val);
        Push_up(i);
        tree[i].len = tree[lson].len + Queryr(rson, mid + 1, r, tree[lson].max);
    }
}

int main()
{
    n = read(), m = read();
    for(int i = 1, l, r; i <= m; ++i) {
        l = read(), r = read();
        a[l] = (1.0 * r / l);
        Seg::Modify(1, 1, n, l, l, r);
        printf("%d\n", Seg::tree[1].len);
    }
    return 0;
}
posted @ 2021-08-04 15:58  Suzt_ilymtics  阅读(43)  评论(0编辑  收藏  举报