1. 程式人生 > 其它 >LG 題解 P4198 樓房重建

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;
}