不錯的一道題。c++
題意:每次修改一棟樓,求這些樓頂跟原點$(0,0)$的斜率單調上升長度(不是$\text{LIS}$)。git
由於一個樓房能被看到能夠等價於它的斜率比以前的任何一個都大。ui
這道題實際上知足區間合併,可是比較麻煩。spa
重點就在$\text{pushup}$的寫法。code
首先定義線段樹中區間的解即爲該對應區間內的單調上升長度。blog
兩個區間的合併:遞歸
如圖,發現右區間會受左區間的影響。get
左區間的答案爲$3$,右區間的答案爲$2$,但在合併後爲$3$。it
而這樣的話合併後爲$4$。class
因此右區間的答案主要受左區間最大值的影響。
令$cnt[o]$爲結點$o$的答案,因此維護中必定有$cnt[o]=cnt[lson]+get\_ans(cnt[rson])$。
如何$get\_ans$呢?
發現右區間受左區間最大值影響,因此必定受到$maxv[lson]$的約束。
觀察以下
若是$B$受到$A$的約束,那麼$C$、$D$也會受到$A$的約束。而後$D$也會受到$C$的約束。
若是$D$受到的約束中$A$沒有$C$強,即$maxv[A]<maxv[C]$,那麼只需遞歸處理$C$的區間再加上$D$貢獻的答案(這裏有個小細節,放後面談)。
由於區間$A,C$中最高的仍爲$maxv[C]$。
反之只需遞歸處理$D$區間,答案無需加上$C$(由於都被$A$擋住了)。
當遞歸到邊界了,也就只剩一個數了$maxv$,只要判斷是否大於約束便可(解爲$0//1$)
遞歸過程當中不須要修改$cnt$,由於區間內無需考慮區間外的影響(要想明白)。
根據以上能夠獲得下面的$\text{pushup}$:
1 int pushup(int o, int l, int r, double d) { 2 if (l == r) return maxv[o]-d > 1e-10 ? 1: 0; 3 int mid = (l + r) >> 1; 4 if (maxv[lson]-d > 1e-10) { // l > d 5 return pushup(lson, l, mid, d) + cnt[o] - cnt[lson]; 6 } else { // l <= d 7 return pushup(rson, mid+1, r, d); 8 } 9 }
$d$表示約束,遞歸時須要下傳。
上面談到了一個問題:加上$D$貢獻的答案。
這裏是
return pushup(lson, l, mid, d) + cnt[o] - cnt[lson];
而不是
return pushup(lson, l, mid, d) + cnt[rson];
要理解二者的差異:
後者是右區間不受左區間約束下的解;而前者是約束後的解$-$左區間的解(由於在一個區間內右區間必定受左區間約束),剩下的必定是約束後的右區間的解。
單點修改時,維護$maxv$時維護區間的$cnt$(這裏由於數值的修改包括在區間中,要對$cnt$進行修改)
也就是:
cnt[o] = cnt[lson] + pushup(rson, mid+1, r, maxv[lson]);
因而問題就解決了。
複雜度分析:單點修改$O(\log n)$,一次$\text{pushup}$須要$O(\log n)$,因此一次的複雜度爲$O(\log^2n)$。
1 #include <bits/stdc++.h> 2 3 using namespace std; 4 5 #define re register 6 #define rep(i, a, b) for (re int i = a; i <= b; ++i) 7 #define repd(i, a, b) for (re int i = a; i >= b; --i) 8 #define For(i, a, b, s) for (re int i = a; i <= b; s) 9 #define maxx(a, b) a = max(a, b) 10 #define minn(a, b) a = min(a, b) 11 #define LL long long 12 #define INF (1 << 30) 13 14 inline int read() { 15 int w = 0, f = 1; char c = getchar(); 16 while (!isdigit(c)) f = c == '-' ? -1 : f, c = getchar(); 17 while (isdigit(c)) w = (w << 3) + (w << 1) + (c ^ '0'), c = getchar(); 18 return w * f; 19 } 20 21 const int maxn = 1e5 + 5, N = 100000; 22 23 double a[maxn]; 24 25 struct SegT { 26 #define lson (o << 1) 27 #define rson (o << 1 | 1) 28 double maxv[maxn << 2]; 29 int cnt[maxn << 2]; 30 void build(int o, int l, int r) { 31 if (l == r) { cnt[o] = 1; return; } 32 int mid = (l + r) >> 1; 33 build(lson, l, mid); build(rson, mid+1, r); 34 } 35 void maintain(int o) { maxv[o] = max(maxv[lson], maxv[rson]); } 36 int pushup(int o, int l, int r, double d) { 37 if (l == r) return maxv[o]-d > 1e-10 ? 1: 0; 38 int mid = (l + r) >> 1; 39 if (maxv[lson]-d > 1e-10) { // l > o 40 return pushup(lson, l, mid, d) + cnt[o] - cnt[lson]; 41 } else { // l <= o 42 return pushup(rson, mid+1, r, d); 43 } 44 } 45 void modify(int o, int l, int r, int x, int y) { 46 if (l == r) { 47 maxv[o] = (double)y / x; 48 cnt[o] = y ? 1 : 0; 49 return; 50 } 51 int mid = (l + r) >> 1; 52 if (x <= mid) modify(lson, l, mid, x, y); else modify(rson, mid+1, r, x, y); 53 maintain(o); 54 cnt[o] = cnt[lson] + pushup(rson, mid+1, r, maxv[lson]); 55 } 56 } T; 57 58 int n, m; 59 int x, y; 60 61 int main() { 62 n = read(); m = read(); 63 rep(i, 1, m) { 64 x = read(), y = read(); 65 T.modify(1, 1, N, x, y); 66 printf("%d\n", T.cnt[1]); 67 } 68 return 0; 69 }