题意:给出一个无向无权图,\(q\) 次询问两点距离,但是假设真实距离为 \(d\),输出 \([d-1,d+1]\) 都视为正确。\(n\le 8000,q\le 10^6,m\le n^2\)。
做法:
正常的最短路肯定是没法做,做出来就得图灵奖了。注意到输出 \(|ans-d|\le 1\) 都可以,我们考虑去找一些关键点出来对整个图跑 bfs,假设 \((x,y)\) 的最短路径经过关键点 \(u\) 或者 \(u\) 的邻域,那么就会有 \(dis(x,u)+dis(y,u)-2\le dis(x,y)\le dis(x,u)+dis(y,u)\)。直接输出 \(dis(x,u)+dis(y,u)-1\) 就可以保证是对的。询问时不太方便去找 \(u\),但是我们发现对所有的关键点取 \(\min\) 肯定是没问题的,不相邻的肯定差的更多,这样输出就可以了。
但是这样显然不够快,还是 \(O(n)\) 个关键点和 \(O(n^2)\) 的 bfs 合起来是 \(n^3\) 的。我们考虑,当我们拉出来一个关键点后,我们就直接把他和他的邻域全删了,不用在后面的 bfs 中跑。这样并没有时间复杂度保证,但是如果我们换成优先选度数更大的点就有保证了,可以证明是 \(O(n^2)\) 的,接下来我们说明一下这个事情。
假设我们做到第 \(i\) 个关键点的时候剩下 \(m_i\) 条边,那么这个关键点目前的度数一定 \(\ge \frac{2m_i}{n}\),也就是我们会同时删除的点的个数。那么我们会有:
第二个等号的原因是因为所有的点和其邻域的点只会被删一次。
那么我们总共 bfs 的复杂度就是 \(O(\sum m_i) = n^2\)。
这样我们可以做到预处理 \(O(n^2)\),询问 \(O(nq)\)。
发现这样预处理和询问非常不协调,考虑平衡一下,我们不拉出来 \(n\) 个关键点,而是选前 \(B\) 个。对于剩下没删掉的点就直接暴力跑出两点距离,输出就直接对暴力的和有关键点的取 \(\min\) 即可,询问复杂度 \(O(qB)\)。
考虑预处理,首先前面的 bfs 部分肯定还是不大于 \(O(n^2)\) 的复杂度,不用管,主要是后面的复杂度。我们注意到,此时每个点的度数最多为 \(\frac{n}B\),因为如果有多的,那么前面每轮至少删掉 \(\frac{n}B\) 个就删空了,这样总边数就是 \(O(\frac{n^2}{B})\) 的,暴力 bfs 复杂度为 \(O(\frac{n^3}{B})\)。
取 \(B = \sqrt{n^3q}\) 即可,复杂度 \(O(n^{1.5}q^{0.5})\)。
代码:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 8005;
int deg[maxn], n, q;
vector<int> e[maxn];
int get_val(char c) {return (isdigit(c) ? c - '0' : c - 'A' + 10);
}
void del(int x) {deg[x] = -1;for (vector<int>::iterator it = e[x].begin(); it != e[x].end(); it++)if(deg[*it] != -1)deg[*it]--;
}
int dis[maxn][maxn];
void bfs(int s) {queue<int> q;dis[s][s] = 0;q.push(s);while(!q.empty()) {int u = q.front(); q.pop();for (vector<int>::iterator it = e[u].begin(); it != e[u].end(); it++) {int v = *it;if(deg[v] != -1 && dis[s][v] > dis[s][u] + 1) {dis[s][v] = dis[s][u] + 1;q.push(v);}}}
}
int p[maxn];
int main() {ios::sync_with_stdio(false);cin >> n >> q;for (int i = 2; i <= n; i++) {string s; cin >> s;for (int j = 1; j < i; j++) {int t = get_val(s[(j - 1) / 4]);if((t >> ((j - 1) % 4)) & 1)e[i].push_back(j), e[j].push_back(i), deg[i]++, deg[j]++;}}memset(dis, 0x3f, sizeof(dis));int B = 100;for (int i = 1; i <= B; i++) {int pos = 0;for (int j = 1; j <= n; j++)if(deg[j] > deg[pos])pos = j;if(pos == 0)break;p[i] = pos;bfs(pos);del(pos);for (vector<int>::iterator it = e[pos].begin(); it != e[pos].end(); it++)if(deg[*it] != -1)del(*it);}for (int i = 1; i <= n; i++) {if(deg[i] == -1)continue;vector<int> res;for (int j = 0; j < e[i].size(); j++)if(deg[e[i][j]] != -1)res.push_back(e[i][j]);swap(res, e[i]);}for (int i = 1; i <= n; i++) {if(deg[i] != -1)bfs(i);}while(q--) {int x, y; cin >> x >> y;int ans = dis[x][y];for (int i = 1; i <= B; i++)if(p[i])ans = min(ans, dis[p[i]][x] + dis[p[i]][y] - 1);cout << ans << '\n';}return 0;
}