两个等号

$0.0\div 0.0=?$

我们建一个凸包。

  1. 1
  2. 2
  3. 3
  4. 4
  5. 5
  6. 6
  7. 7
  8. 8
  9. 9
  10. 10
  11. 11
  12. 12
  13. 13
  14. 14
  15. 15
  16. 16
  17. 17
  18. 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; } };

本题中保证了 $[f,f+n)$ 区间中的直线斜率不增,因此构建下凸包可直接倒序插入单调栈。

但很遗憾,截距 $b$ 不是互不相同的。这将导致可能插入两条完全相同的直线——p.b > g[m-1].b。于是后方弹出时inter (g[m-1], g[m-2])返回nan,建出的凸包是错的。

所以应该改成p.b >= g[m-1].b

new[], delete[]

我们将树作长链剖分。这棵树的节点在 $[0,2n)$ 范围内,我们还钦定 $2n$ 是虚拟根节点。存储时我们令 $x$ 的指针 $f(x)$ 指向重儿子 $y$ 的指针减一,也即 $f(x)=f(y)-1$。在链的末端 $z$ 用 operator new[] 申请大小是 $l+1$ 的数组,并令 $f(z)$ 是该数组的首位置指针 $+l$。因此,直接对于每条链的链顶节点 $x$ 施 delete[] f[x],就能释放这条链占用的空间。

  1. 1
  2. 2
  3. 3
  4. 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];

我们写了 $<$ 而非 $\leq$,没有删除 $2n$ 的所有轻儿子的数组。不幸,这个过程会被调用约 $500$ 次,而 $n$ 在 $10^5$ 级别。我们成功地占用了多达 $4\text{ GB}$ 的空间,并因此少了 $20\text{ pts}$。

改进的长剖写法

使用静态数组空间而非堆分配每个数组。例如 int arr[N<<1], *pt = arr,其中 $N$ 是 $n$ 的最大值;并将每个 new int [n] 替换成 pt; pt += n

  • 2023年11月3日