有的最短路题目中,可能边的数目很大,朴素建图边的数目为 O ( ∣ V ∣ 2 ) O(|V|^2) O(∣V∣2)。这样很难直接应用 Dijkstra 算法等。
对付这种图,一般采用的策略是:
- 去除无用选项。有可能两点之间直接连接的路径长一定不是最短路径,这样就可以排除大多数路径,只构造那些有用的边。
- 构造等价选项。有可能两点之间的路径长完全等价于按照某种简单模式形成的路径长,这样就可以考虑这种简单的模式。
- 优化建图方式。例如建立虚拟点,以代替完全子图中各个点之间的相互连边。
例 1:最短路 1
题意:有 n n n 个点, i i i 号点和 j j j 号点之间无向边的边权为 i xor j i \operatorname{xor} j ixorj,求 1 1 1 号点到 n n n 号点的最短路。(原题:HDU 6713)
由于 i xor j xor j xor k = i xor k ≤ i xor j + j xor k i\operatorname{xor}j\operatorname{xor}j\operatorname{xor}k = i\operatorname{xor}k \le i \operatorname{xor} j + j\operatorname{xor}k ixorjxorjxork=ixork≤ixorj+jxork,因此直接从 i i i 到 k k k 一定比 i i i 到 j j j 再到 k k k 更优。所以答案就是 1 xor n 1 \operatorname{xor} n 1xorn。
例 2:最短路
题意:给定一张边带权有向图,除了已有的边外,两个点之间还可以走异或边,权重为一个常数乘以两点编号的异或值。求指定两点的最短路。(原题:LOJ 6354)
由于两个点之间的异或可以看作是一个点删掉某几位的 1、加上某几位的 1 得到,因此可以按位这种简单的模式建边。然后跑最短路即可。
priority_queue<pair<int, int> > pq;
int n, m, C, S, T;
int to[2200005], at[100005] = {
0}, cnt = 0, nxt[2200005], w[2200005];
int dis[100005];
void init(){
n = read(), m = read(), C = read();
for (int i = 1; i <= m; ++i){
int u = read(), v = read(), ww = read();
w[++cnt] = ww;
to[cnt] = v, nxt[cnt] = at[u], at[u] = cnt;
}
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; j <<= 1)
if