各种优化建图(留坑)

一、线段树优化

1.

\(n\) 个点,\(m\) 个操作:

  • 1 u l r w \(u\) 向区间 \([l, r]\) 的节点连长度为 \(w\) 的边;

  • 2 u l r w 区间 \([l, r]\) 的节点向 \(u\) 连长度为 \(w\) 的边;

  • 3 x y l r w 区间 \([x, y]\) 的节点向区间 \([l, r]\) 的节点连长度为 \(w\) 的边。

\(1\)\(n\) 的最短路。

  • \(n, m\le 10^5\)

直接暴力连可使边数达到 \(\Omicron(n^2m)\)

使用线段树优化,将线段树上每一个节点当成这一段区间。

建两颗线段树 \(A\)\(B\)\(A\) 中父亲向儿子连长度为 \(0\) 的边,\(B\) 中儿子向父亲连长度为 \(0\) 的边,二者共用叶子节点,叶子节点编号为 \(1\sim n\)

  1. \(u\)\(A\) 上构成 \([l, r]\) 的节点连长度为 \(w\) 的边;
  2. \(B\) 上构成 \([l, r]\) 的节点向 \(u\) 连长度为 \(w\) 的边;
  3. 建一个虚点,\(B\) 上构成 \([x, y]\) 的节点向虚点连长度为 \(w\) 的边,虚点向 \(A\) 上构成 \([l, r]\) 的节点连长度为 \(0\) 的边。

这样可将边数降至 \(\Omicron(m\log n + n)\),点数为 \(\Omicron(n + m)\) 级别,用二叉堆实现的 \(\rm Dijkstra\) 可做到 \(\Omicron(m\log^2 n)\)

CF786B Legacy(模板,最短路)Code

2.

P5025 [SNOI2017]炸弹

(注意 \(x_i\) 已经排过序了)

对于每一个炸弹,使用 \(\rm lower\_bound\)\(\rm upper\_bound\) 算出它能引爆的炸弹编号范围,与上题一样用线段树连边。

随后 \(\rm Tarjan\) 缩点,并顺便维护每个强连通分量内能引爆的编号的左右端点 \(l_i, r_i\),那么只要引爆强连通分量内的任意一个节点,这一段区间内都会被引爆,因为强连通分量内部可以全部互相引爆。

最后再对缩点后的图进行 \(\rm Dfs\),当节点 \(u\) 指向节点 \(v\) 时,有

\[l_u \gets \min(l_u, l_v) \\ r_u \gets \max(r_u, r_v) \]

原因是 \(u\) 爆炸可以引爆 \(v\)

对于炸弹 \(i\),如果它属于强连通分量 \(u\),则引爆 \(i\) 会导致 \(r_u - l_u + 1\) 个炸弹爆炸。

Code

3.

P3588 [POI2015] PUS

注意题目规定数的范围是 \([1, 10^9]\)

由于 \(a_i\) 是正整数,所以由

\[a_x > a_y \]

\[a_x \ge a_y +1 \]

这就是一个差分约束了,使用拓扑排序解决(可以判环),首先照抄线段树区间连边(\(k\) 个大的数将 \([l,r]\) 分成若干段,分段处理,大的与小的分别连虚拟点),然后令 \(dp_u\) 表示节点 \(u\) 的最小值,初始值所有均为 \(1\)(范围内最小值),如果数已知则为已知值。

当节点 \(u\) 指向节点 \(v\) 时,

\[dp_v = \max(dp_v, dp_u + dis_{u, v}) \]

无解有 \(3\) 种情况:

  1. 出现环(用 \(vis\) 值判);
  2. 超出范围;
  3. \(a_u\) 有已知值,但 \(dp_u > a_u\)

注意最后只判断叶子节点(即节点 \(1\sim n\),区间长度为 \(1\) 的节点)。

Code

二、 前后缀优化

1.

\(n\) 个点,\(m\) 个操作:

  • 1 u l r w \(u\) 向区间 \([l, r]\) 以外的节点连长度为 \(w\) 的边;
  • 2 u l r w 区间 \([l, r]\) 以外的节点向 \(u\) 连长度为 \(w\) 的边;
  • 3 x y l r w 区间 \([x, y]\) 以外的节点向区间 \([l, r]\) 以外的节点连长度为 \(w\) 的边。

\(1\)\(n\) 的最短路。

  • \(n, m\le 5\times 10^5\)

线段树是 \(\Omicron(m\log^2 n)\) 的,过不去。

区间 \([l, r]\) 以外 相当于 前缀 \([1, l - 1]\)与后缀 \([r + 1, n]\)

新建 \(n\) 个点 \(pre_i\) 表示前缀 \([1, i]\),再新建 \(n\) 个点 \(suf_i\) 表示后缀 \([i, n]\)

\(pre_i\) 分别向 \(i\)\(pre_{i - 1}\) 连长度为 \(0\) 的边,从 \(suf_i\) 分别向 \(i\)\(suf_{i + 1}\) 连长度为 \(0\) 的边。

  1. \(u\)\(pre_{l - 1}\)\(suf_{r + 1}\) 连长度为 \(w\) 的边;
  2. \(pre_{l - 1}\)\(suf_{r +1}\)\(u\) 连长度为 \(w\) 的边;
  3. \(pre_{x - 1}\)\(suf_{y + 1}\) 分别向 \(pre_{l - 1}\)\(suf_{r +1}\) 连长度为 \(w\) 的边。

点数为 \(\Omicron(n)\) 级别,边数为 \(\Omicron(n + m)\) 级别,\(\rm Dijkstra\) 可做到 \(\Omicron((n + m)\log n)\),足已通过。

2.

P6378 [PA2010] Riddle

\(\rm 2-SAT\) 解决,每个点有选与不选两种,用 \(u\)\(u’\) 表示。

以下连一条边 \(u\to v\) 指连双向边, \(u\) 符合则 \(v\) 必定符合,\(v\) 不符合则 \(u\) 必定不符合(在图中连 \(2\) 条边 \((u\to v), (v'\to u')\))。

由于原图中一条边 \((u, v)\) 的两个端点至少选一个,所以要连边 \(u'\to v\)\(u\) 不选则 \(v\) 必选,\(v\) 不选则 \(u\) 必选),注意不能是 \(u\)\(v’\) 连边,因为是 至少 选一个。

代表前缀 \([1, i]\) 的点 \(pre_i\) 表示在该部分中前 \(i\) 个点是否有被选的。

连边:

\[a_i\to pre_{a_i} \\ pre_{a_i} \to pre_{a_{i - 1}} \\ pre_{a_{i - 1}} \to a_i' \]

然后跑 \(\rm Tarjan\) 并判断即可。

Code

3.

CF1215F Radio Stations

仍然是 \(\rm 2-SAT\)\(u\)\(u'\) 分别表示电站 \(u\) 开不开。

至少一个就是 \(u’\to v\),至多一个则是 \(u\to v'\)

新建立 \(2(m + 1)\) 个点,分别表示当 \(k\)\(0\sim m\)\(f\le k\) 是否成立,记为 \(a_i\)

对于一个电站 \(u\),连边:

\[u\to a_{l_u - 1}' \\ u\to a_{r_u} \\ \]

跑完 \(\rm Tarjan\) 后进行判断无解就不说了。

\(bel_u < bel_{u'}\) 时,说明电站 \(u\) 是开的,电站个数加一;

\(bel_{a'_{k - 1}} < bel_{a_{k - 1}}\)\(bel_{a_k} < bel_{a'_k}\) 时,说明 \(f > k - 1\)\(f\le k\),那么 \(k\) 就是其中一个满足条件的 \(f\),输出。

最后注意 \(f\) 是不能取 \(0\) 的,处理方法是连边 \(a_0\to a'_0\),这样保证了 \(f\le 0\) 是一定不符合的,否则会出现矛盾。

Code

posted @ 2022-07-01 11:47  mango09  阅读(85)  评论(0编辑  收藏  举报
-->