两个等号

0.0÷0.0=?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)[f,f+n) 区间中的直线斜率不增,因此构建下凸包可直接倒序插入单调栈。

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

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

new[], delete[]

我们将树作长链剖分。这棵树的节点在 [0,2n)[0,2n) 范围内,我们还钦定 2n2n 是虚拟根节点。存储时我们令 xx 的指针 f(x)f(x) 指向重儿子 yy 的指针减一,也即 f(x)=f(y)1f(x)=f(y)-1。在链的末端 zzoperator new[] 申请大小是 l+1l+1 的数组,并令 f(z)f(z) 是该数组的首位置指针 +l+l。因此,直接对于每条链的链顶节点 xxdelete[] 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,没有删除 2n2n 的所有轻儿子的数组。不幸,这个过程会被调用约 500500 次,而 nn10510^5 级别。我们成功地占用了多达 4 GB4\text{ GB} 的空间,并因此少了 20 pts20\text{ pts}

改进的长剖写法

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

  • 2023年11月3日