LG 題解 P4198 樓房重建
阿新 • • 發佈:2021-08-04
背後即是祖國,我們無路可退。
棟樓的斜率。
前置芝士
- 主席樹
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 > 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;
}