洛谷 P4198 樓房重建
阿新 • • 發佈:2020-08-08
思路
此題可轉化為以下模型
給定序列\(a[1...n]\),支援單點修改,每次求區間單調棧大小
\(n,Q\le 10^5\)
區間單調棧是什麼呢?對於一個區間,建立一個棧,首先將第一個元素入棧,從左往右掃,如果當前元素大於等於棧頂元素,就將其入棧,由此形成的棧即為單調不減的區間單調棧。
轉化一下,其實就是求區間內滿足\(a[i]=\max\limits_{j=1}^ia[j]\)的\(a[i]\)的個數。
一個自然的想法是維護單調棧的大小\(siz\),那麼如何去進行區間的合併呢?
合併兩個子區間時,假設左子區間為\(L\),右子區間為\(R\),考慮合併之後的單調棧的組成部分:
-
第一部分:\(L\)
因為單調棧是從左往右做的,所以\(L\)的單調棧必然是大區間單調棧的一部分
-
剩餘部分
設出函式\(calc(now,pre)\),\(now\)表示當前節點,\(pre\)表示當前單調棧的棧頂,\(calc\)函式計算剩餘部分的單調棧的大小
總的單調棧大小\(siz\)就是\(L_{siz}+calc(R,L_{max})\)
calc的實現
現在有\(calc(now,pre)\),\(l\)表示\(now\)的左子樹,\(r\)表示\(now\)的右子樹
- 如果\(pre>l_{max}\),說明整個左子區間都不用考慮了,此時答案就變成了\(calc(r,pre)\)
- 如果\(pre\le l_{max}\),此時\(l\)是有貢獻的,他對\(siz\)的貢獻就是\(calc(l,pre)\),右子樹的貢獻為\(calc(r,l_{max})\),總貢獻就是\(calc(l,pre)+calc(r,l_{max})\)
至此\(calc\)就推完了,但是我們發現如果僅僅是這樣的話,在最壞的情況下,複雜度會爆炸,那麼怎麼優化呢?
觀察\(calc(r,l_{max})\),發現它就等於\(siz-l_{siz}\),所以第二種情況就可以變成\(calc(l,pre)+siz-l_{siz}\),其中\(siz\)都是可以處理好的
這樣我們就可以在\(O(\log n)\)
總時間複雜度\(O(Q\log^2 n)\)
程式碼
/*
Author:loceaner
*/
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define lson rt << 1
#define rson rt << 1 | 1
using namespace std;
const int A = 1e5 + 11;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;
inline int read() {
char c = getchar();
int x = 0, f = 1;
for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
return x * f;
}
double val;
int n, m, ans, pos;
struct node { double maxn; int siz; } a[A << 2];
void build(int rt, int l, int r) {
if (l == r) return;
int mid = (l + r) >> 1;
build(lson, l, mid), build(rson, mid + 1, r);
}
inline int calc(int rt, int l, int r, double h) {
if (l == r) return a[rt].maxn > h;
int mid = (l + r) >> 1;
if (a[lson].maxn <= h) return calc(rson, mid + 1, r, h);
return calc(lson, l, mid, h) + a[rt].siz - a[lson].siz;
}
inline void update(int rt, int l, int r) {
if (l == r) {
a[rt].maxn = val, a[rt].siz = 1;
return;
}
int mid = (l + r) >> 1;
if (pos <= mid) update(lson, l, mid);
else update(rson, mid + 1, r);
a[rt].maxn = max(a[lson].maxn, a[rson].maxn);
a[rt].siz = a[lson].siz + calc(rson, mid + 1, r, a[lson].maxn);
}
int main() {
n = read(), m = read();
build(1, 1, n);
while (m--) {
int x = read(), y = read();
pos = x, val = (double) y / x;
update(1, 1, n);
cout << a[1].siz << '\n';
}
}