1. 程式人生 > >【WinterCamp 2013】樓房重建(線段樹維護動態單調棧)

【WinterCamp 2013】樓房重建(線段樹維護動態單調棧)

Problem

  • 題意就是求一個支援修改的單調棧長度.

  • 修改次數 m m ,序列長度 n n .

Data constraint

  • n , m 100000 n,m\le 100000

Solution

  • 這是一個經典模型.

  • 這個是用線段樹來實現的,先不妨假設現在要求上升子序列長度.

  • 那麼如果我們要修改,則直接線上段樹上找到對應位置,然後進行修改即可.

  • 對於線段樹每個節點,維護一個 A n s Ans (這個區間單調棧的長度),一個 v

    v (這個區間內數的最大值)

  • 現在,當只有最左邊一個數時,單調棧長度顯然為 1 1 .

  • 那麼歸納,假設左邊的單調棧已經構好,現在需要計算當前節點的答案.

  • 那麼顯然 當前答案 = 左邊的答案 + 左邊最大的數放在 M + 1 e n M+1\sim en 這段區間內得到的單調棧長度

  • 而形如 X = Y + Z X=Y+Z 的這個式子,右邊的 Z Z 依舊可以 l o g log 的時間複雜度內計算出來,做法類似.

  • 不妨設 f ( x , y , z ) f(x,y,z) 表示在 [ x , y ] [x,y] 這段閉區間形成的單調棧棧頂加入 z z 這個點形成的新單調棧長度, L s , R s Ls,Rs 分別表示左右兒子, n o w now 表示當前點.

  • 那麼依舊是討論,假設左區間 [ x , M ] [x,M] 的最大值小於等於插入的數,那麼貢獻顯然是 f ( M + 1 , e n , z ) f(M+1,en,z)

  • 否則貢獻是 ( t r [ n o w ] . v t r [ L s ] . v ) + f ( s t , M , v ) (tr[now].v-tr[Ls].v) + f(st,M,v) ,這裡是整個演算法最巧妙的一處.

  • 因為整個過程是線上段樹上進行的,所以當計算到 x x 這個點的答案時,其所有兒子的答案都已經計算完畢了.

  • 那麼因為 t r [ n o w ] . v = t r [ L s ] . v + f ( M + 1 , e n , t r [ L s ] . v ) tr[now].v=tr[Ls].v+f(M+1,en,tr[Ls].v) ,所以實際上 f ( M + 1 , e n , t r [ L s ] . v ) f(M+1,en,tr[Ls].v) 是等價於 t r [ n o w ] . v t r [ L s ] . v tr[now].v-tr[Ls].v .

  • 所以整個演算法就是 O ( n l o g 2 n ) O(nlog^2n) 的。

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