题意
给一个无向图,求它的补图中,每个点到S点的最短路。输入点数N<=2*10^5,边数M<=2*10^4。
思路
原图边很少,所以补图边很多。那么有相当多的点最短路为1,而且最短路的长度也会很小。那么我们可以一层一层的求出最短路。先从S求出距离为1的点,即原图不存在(1,u)。然后再从距离为1的点集向距离未知的点集更新。更新的过程就是扫边表,如果有一个跨两个集合的原图边,就把未知点的cnt++。如果未知集合的某个点cnt加到了距离1集合的点数,那么它就不是距离为2的点。于是我们把未知集合分为距离为2的集合和距离位置的集合,再如法炮制,用距离为2的集合更新位置集合得到距离为3的集合。。。
这样的时间复杂度并不大,如果N>M那么所有的最短路都不超过1。最复杂的情况莫过于为补图构造一些或者很多很长的最短路,但是n个点完全图就有n(n-1)/2条边,要构造顶多长度也超不过200。而点数一少复杂度就很明显的下降了。
AC代码 C++
#include <stdio.h>
#include <string.h>
#include <vector>
#include <set>
using namespace std;
#define MAXN 200022
vector<int> g[MAXN];
int q[MAXN << 1];
int tmp[MAXN];
int ans[MAXN];
int cnt[MAXN][2];
int main()
{
int T, clock = 1, n, m, u, v, S, i, cur, step, hd, tl, TL, sz;
set<int> st[2];
set<int>::iterator it;
scanf("%d", &T);
while (T--)
{
memset(ans, -1, sizeof(ans));
scanf("%d%d", &n, &m);
st[0].clear();
st[1].clear();
for (i = 1; i <= n; i++)
{
st[0].insert(i);
g[i].clear();
}
while (m-- && scanf("%d%d", &u, &v) > 0)
{
g[u].push_back(v);
g[v].push_back(u);
}
scanf("%d", &S);
st[0].erase(S);
hd = tl = 0;
q[tl++] = S;
ans[S] = 0;
clock++;
for (cur = 0, step = 1, TL = 0; tl != TL; cur = 1 - cur, step++, clock++)
{
TL = tl;
st[1 - cur].clear();
sz = TL - hd;
while (hd < TL)
for (i = 0, u = q[hd++]; i < g[u].size(); i++)
if (ans[v = g[u][i]] == -1)
if (cnt[v][0] != clock)
{
cnt[v][0] = clock;
cnt[v][1] = 1;
}
else
cnt[v][1]++;
for (it = st[cur].begin(), m = 0; it != st[cur].end(); tmp[m++] = *it++);
for (i = 0, u = tmp[0]; i<m; u = tmp[++i])
if (cnt[u][0] == clock && cnt[u][1] == sz)
{
st[1 - cur].insert(u);
st[cur].erase(u);
}
else
{
ans[u] = step;
q[tl++] = u;
}
}
for (i = 1, cur = 0; i <= n; i++)
if (i != S)
tmp[cur++] = ans[i];
for (i = 0; i < cur; i++)
printf("%d%c", tmp[i], "\n "[i + 1 != cur]);
}
return 0;
}