【WinterCamp 2013】樓房重建(線段樹維護動態單調棧)
阿新 • • 發佈:2018-12-10
Problem
-
題意就是求一個支援修改的單調棧長度.
-
修改次數 ,序列長度 .
Data constraint
Solution
-
這是一個經典模型.
-
這個是用線段樹來實現的,先不妨假設現在要求上升子序列長度.
-
那麼如果我們要修改,則直接線上段樹上找到對應位置,然後進行修改即可.
-
對於線段樹每個節點,維護一個 (這個區間單調棧的長度),一個 (這個區間內數的最大值)
-
現在,當只有最左邊一個數時,單調棧長度顯然為 .
-
那麼歸納,假設左邊的單調棧已經構好,現在需要計算當前節點的答案.
-
那麼顯然 當前答案 = 左邊的答案 + 左邊最大的數放在 這段區間內得到的單調棧長度
-
而形如 的這個式子,右邊的 依舊可以 的時間複雜度內計算出來,做法類似.
-
不妨設 表示在 這段閉區間形成的單調棧棧頂加入 這個點形成的新單調棧長度, 分別表示左右兒子, 表示當前點.
-
那麼依舊是討論,假設左區間 的最大值小於等於插入的數,那麼貢獻顯然是
-
否則貢獻是 ,這裡是整個演算法最巧妙的一處.
-
因為整個過程是線上段樹上進行的,所以當計算到 這個點的答案時,其所有兒子的答案都已經計算完畢了.
-
那麼因為 ,所以實際上 是等價於 .
-
所以整個演算法就是 的。
#include <cstdio>
#define F(i, a, b) for (int i = a; i <= b; i ++)
#define max(a, b) ((a) > (b) ? (a) : (b))
#define M (st + en >> 1)
#define Ls (x << 1)
#define Rs (Ls | 1)
using namespace std;
int n, m, X, Y;
struct node { double v; int ans; } F[400000];
int Calc(int x, int st, int en, double v) {
if (st == en)
return F[x].v > v;
else
return F[Ls].v <= v ? Calc(Rs, M + 1, en, v) : F[x].ans - F[Ls].ans + Calc(Ls, st, M, v);
}
void Make(int x, int st, int en, int p, double v) {
if (st == en)
F[x] = {v, 1};
else
M >= p ? Make(Ls, st, M, p, v) : Make(Rs, M + 1, en, p, v),
F[x].v = max(F[Ls].v, F[Rs].v), F[x].ans = F[Ls].ans + Calc(Rs, M + 1, en, F[Ls].v);
}
int main() {
scanf("%d%d", &n, &m);
F(i, 1, m)
scanf("%d%d", &X, &Y),
Make(1, 1, n, X, (double) Y / X),
printf("%d\n", F[1].ans);
}