1. 程式人生 > 實用技巧 >P4198 樓房重建

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