楼房重建

不错的一道题。

题意:每次修改一栋楼,求这些楼顶跟原点$(0,0)$的斜率单调上升长度(不是$\text{LIS}$)。

因为一个楼房能被看到可以等价于它的斜率比之前的任何一个都大。

这道题实际上满足区间合并,但是比较麻烦。

重点就在$\text{pushup}$的写法。

首先定义线段树中区间的解即为该对应区间内的单调上升长度。

两个区间的合并:

如图,发现右区间会受左区间的影响。

左区间的答案为$3$,右区间的答案为$2$,但在合并后为$3$。

而这样的话合并后为$4$。

所以右区间的答案主要受左区间最大值的影响。

令$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 }

 

posted @ 2019-03-16 11:37  AC-Evil  阅读(401)  评论(2编辑  收藏  举报