我们建一个凸包。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
struct block { line g[S], *f; int t, n, m; inl void build () { // k's of lines in [f, f + n) should be non-increasing. m = t = 0; for (int i = 0; i < n; ++i) { const auto &p = f[n - i - 1]; if (p.b == -INF) continue; while (m > 1 && (p.k == g[m-1].k ? p.b > g[m-1].b : inter (g[m-1], g[m-2]) >= inter (p, g[m-1]))) --m; g[m++] = p; } } inl ll operator () (ll x) { // x's input should be non-decreasing. while (t + 1 < m && g[t] (x) <= g[t + 1] (x)) ++t; return t < m ? g [t] (x) : -INF; } };
本题中保证了 区间中的直线斜率不增,因此构建下凸包可直接倒序插入单调栈。
但很遗憾,截距 不是互不相同的。这将导致可能插入两条完全相同的直线——p.b > g[m-1].b
。于是后方弹出时inter (g[m-1], g[m-2])
返回nan
,建出的凸包是错的。
所以应该改成p.b >= g[m-1].b
。
new[], delete[]
我们将树作长链剖分。这棵树的节点在 范围内,我们还钦定 是虚拟根节点。存储时我们令 的指针 指向重儿子 的指针减一,也即 。在链的末端 用 operator new[]
申请大小是 的数组,并令 是该数组的首位置指针 。因此,直接对于每条链的链顶节点 施 delete[] f[x]
,就能释放这条链占用的空间。
- 1
- 2
- 3
- 4
for (int x = 0; x < n<<1; ++x) for (int v = head[x]; ~v; v = nxt[v]) delete[] f[v]; delete[] f[n<<1];
我们写了 而非 ,没有删除 的所有轻儿子的数组。不幸,这个过程会被调用约 次,而 在 级别。我们成功地占用了多达 的空间,并因此少了 。
改进的长剖写法
使用静态数组空间而非堆分配每个数组。例如 int arr[N<<1], *pt = arr
,其中 是 的最大值;并将每个 new int [n]
替换成 pt; pt += n
。