各种优化建图(留坑)
一、线段树优化
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\)。
- \(u\) 向 \(A\) 上构成 \([l, r]\) 的节点连长度为 \(w\) 的边;
- \(B\) 上构成 \([l, r]\) 的节点向 \(u\) 连长度为 \(w\) 的边;
- 建一个虚点,\(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.
(注意 \(x_i\) 已经排过序了)
对于每一个炸弹,使用 \(\rm lower\_bound\) 与 \(\rm upper\_bound\) 算出它能引爆的炸弹编号范围,与上题一样用线段树连边。
随后 \(\rm Tarjan\) 缩点,并顺便维护每个强连通分量内能引爆的编号的左右端点 \(l_i, r_i\),那么只要引爆强连通分量内的任意一个节点,这一段区间内都会被引爆,因为强连通分量内部可以全部互相引爆。
最后再对缩点后的图进行 \(\rm Dfs\),当节点 \(u\) 指向节点 \(v\) 时,有
原因是 \(u\) 爆炸可以引爆 \(v\)。
对于炸弹 \(i\),如果它属于强连通分量 \(u\),则引爆 \(i\) 会导致 \(r_u - l_u + 1\) 个炸弹爆炸。
3.
注意题目规定数的范围是 \([1, 10^9]\)。
由于 \(a_i\) 是正整数,所以由
得
这就是一个差分约束了,使用拓扑排序解决(可以判环),首先照抄线段树区间连边(\(k\) 个大的数将 \([l,r]\) 分成若干段,分段处理,大的与小的分别连虚拟点),然后令 \(dp_u\) 表示节点 \(u\) 的最小值,初始值所有均为 \(1\)(范围内最小值),如果数已知则为已知值。
当节点 \(u\) 指向节点 \(v\) 时,
无解有 \(3\) 种情况:
- 出现环(用 \(vis\) 值判);
- 超出范围;
- \(a_u\) 有已知值,但 \(dp_u > a_u\)。
注意最后只判断叶子节点(即节点 \(1\sim n\),区间长度为 \(1\) 的节点)。
二、 前后缀优化
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\) 的边。
- \(u\) 向 \(pre_{l - 1}\) 和 \(suf_{r + 1}\) 连长度为 \(w\) 的边;
- \(pre_{l - 1}\) 和 \(suf_{r +1}\) 向 \(u\) 连长度为 \(w\) 的边;
- \(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.
用 \(\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\) 个点是否有被选的。
连边:
然后跑 \(\rm Tarjan\) 并判断即可。
3.
仍然是 \(\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\),连边:
跑完 \(\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\) 是一定不符合的,否则会出现矛盾。