Time limit: 2 seconds

Memory limit: 1024 megabytes

In ICPCCamp, there are n cities and m (bidirectional) roads between cities. The i-th road is between the

ai-th city and the bi-th city. There may be roads connecting a citie to itself and multiple roads between

the same pair of cities.

Bobo has q travel plans. The i-th plan is to travel from the ui-th city to the vi-th city. He would like

to know the smallest number of roads needed to travel for each plan. It is guaranteed that cities are

connected.

【Input】

The first line contains 3 integers n, m, q (1 ≤ n ≤ 10^5, 0 < m − n < 100, 1 ≤ q ≤ 10^5).

The i-th of the following m lines contains 2 integers ai, bi (1 ≤ ai, bi ≤ n).

The i-th of the last q lines contains 2 integers ui, vi (1 ≤ ui, vi ≤ n)

【Output】

n lines with integers l1, l2, … , ln. li denotes the smallest number of roads travelling from city ui to city vi.

Examples

【standard input 1】

4 5 3

1 2

1 3

1 4

2 3

3 4

2 2

2 3

2 4

【standard output 1】

0

1

2

【standard input 1】

1 2 1

1 1

1 1

1 1

【standard output 1】

0

【题解】

毒题。

讲一下做法:

注意到m最多为n+100最小为n

我们先在m条边中取出n-1条。让它组成一棵树。(用并查集去除多余的边,但是这些多余的边不能删掉要记录下来);

对于每一个询问x,y;先求出它们的最近公共祖先z;然后设某个节点x的深度为dep[x]

则两个点之间的最短路一定是dep[x]+dep[y]-2*dep[z];

但是这只是在去掉了多余的边后的情况。

我们还要把那些边再加进来。

全部都加进来之后。

从加进来的多余的边的端点开始进行spfa。

设询问仍然是x,y;

则再尝试用dis[s][x]+dis[s][y]来更新答案。其中s∈{x|x为多余边的端点}

这里的dis第一维只要开到200多就可以了。

相当于我们在一棵树上进行了一次最短路。

然后再用多余的边上的点作为起点进行最短路。

如果那些多余边上的点是最短路上的点。那么在第二次更新dis[s][x]+dis[s][y]的时候都会涉及到。

可以理解为x->y经过了s这个点的最短路。枚举s。取最小。

添加了边。最短路发生改变。那么新的最短路肯定经过某一条新添加的边的两端点。

很棒的思路。

最后是LCA的实现思路:

盗了几张图:

【7.89%】【BNUOJ 52303】Floyd-Warshall_最短路

【7.89%】【BNUOJ 52303】Floyd-Warshall_#include_02

我发两份代码:

第一份用的是RMQ预处理出LCA的方法。可以AC;

第二份用的是树上倍增的方法,但是TLE。(供学习);

//代码一,可AC
#include <cstdio>
#include <iostream>
#include <queue>
#include <algorithm>
#include <vector>

using namespace std;

const int MAXN = 4e5 + 10;
const int MAX_REST = 100 + 10;
const int INF = 2100000000;

int n, m, q, ans[MAXN], dep[MAXN] = { 0 };
int pr[25];
queue <int> dl;
int dis[MAX_REST * 2][MAXN];

struct bian
{
int x, y;
};

vector <int> a[MAXN], c;
vector < pair<int, int> > rest, b;
int f[MAXN], re = 0;
int p[MAXN][25];
int fi[MAXN], cnt = 0;
int shall[MAXN * 2];

void input(int &num)
{
num = 0;
char c;
do
{
c = getchar();
} while (!isdigit(c));
while (isdigit(c))
{
num = num * 10 + c - '0';
c = getchar();
}
}

int ff(int x)
{
if (f[x] == x)
return f[x];
else
f[x] = ff(f[x]);
return f[x];
}

int lca(int x, int y)
{
int xx = fi[x];
int yy = fi[y];
if (xx > yy)
swap(xx, yy);
int k = shall[yy - xx + 1];
int a1 = p[xx][k];
int a2 = p[yy - pr[k] + 1][k];//左边和右边都搞下才能覆盖整个区间
if (dep[a1] < dep[a2])
return a1;
else
return a2;
}

void dfs(int u, int fa)
{
dep[u] = dep[fa] + 1;
p[++cnt][0] = u;//dfs序列为cnt,长度为2^0的深度最小的节点
fi[u] = cnt;
int len = a[u].size();
for (int i = 0; i <= len - 1; i++)
{
int y = a[u][i];
if (y != fa)
{
dfs(y, u);
p[++cnt][0] = u;
}
}
}

void spfa(int s)
{
for (int i = 1; i <= n; i++)//用memset会比较慢
dis[s][i] = INF;
dis[s][c[s]] = 0;
dl.push(c[s]);
while (!dl.empty())
{
int x = dl.front();
dl.pop();
int len = a[x].size();
for (int i = 0; i <= len - 1; i++)
{
int y = a[x][i];
if (dis[s][y] >dis[s][x] + 1)
{
dis[s][y] = dis[s][x] + 1;
dl.push(y);
}
}
}
}

int main()
{
//freopen("F:\\rush.txt", "r", stdin);
input(n); input(m); input(q);
for (int i = 1; i <= n; i++)
f[i] = i;
for (int i = 1; i <= m; i++)
{
int x, y;
input(x); input(y);
if (x == y)
continue;
int r1 = ff(x), r2 = ff(y);
if (r1 != r2)
{
f[r1] = r2;
a[x].push_back(y);
a[y].push_back(x);
}
else
rest.push_back(make_pair(x, y));
}
dfs(1, 0);
pr[0] = 1;
for (int i = 1; i <= 24; i++)
pr[i] = pr[i - 1] << 1;
shall[1] = 0;
int now = 1;
for (int i = 2; i <= cnt; i++)//shall[i]表示长度为i的区间需要2的多少次方来覆盖。
{
if (i == pr[now])
{
shall[i] = shall[i - 1] + 1;
now++;
}
else
shall[i] = shall[i - 1];
}
for (int i = 1; pr[i] <= cnt; i++)
for (int j = 1; j + pr[i] - 1 <= cnt; j++)//用RMQ来做
{
int x = p[j][i - 1];
int y = p[j + pr[i - 1]][i - 1];
if (dep[x] < dep[y])
p[j][i] = x;
else
p[j][i] = y;
}
for (int i = 1; i <= q; i++)
{
int x, y;
input(x); input(y);
b.push_back(make_pair(x, y));
ans[i] = dep[x] + dep[y] - 2 * dep[lca(x, y)];
}
int len = rest.size();//把多余的边加入图中
for (int i = 0; i <= len - 1; i++)
{
int x = rest[i].first, y = rest[i].second;
a[x].push_back(y);
a[y].push_back(x);
c.push_back(x);
c.push_back(y);//并取出边的两端点。用来作为spfa的起点
}
len = c.size();
for (int i = 0; i <= len - 1; i++)
spfa(i);
for (int i = 0; i <= q - 1; i++)
{
int x, y;
x = b[i].first, y = b[i].second;
for (int j = 0; j <= len - 1; j++)//需要更新的肯定是那些新添加的点。
{
int s = j;
ans[i + 1] = min(ans[i + 1], dis[s][x] + dis[s][y]);
}
printf("%d\n", ans[i + 1]);
}
return 0;
}
//代码二 会超时 树上倍增
//递推公式:p[i][j] = p[p[i][j-1][j-1];
//p[i][j]表示从i节点往上走2^j个节点后是什么节点。
#include <cstdio>
#include <queue>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
const int MAXN = 4e5;
const int MAX_REST = 100 + 10;
int n, m, q, ans[MAXN], dep[MAXN] = { 0 };
int pr[20];
queue <int> dl;
int dis[MAX_REST*2][MAXN];
struct bian
{
int x, y;
};
vector <int> a[MAXN],c;
vector < pair<int, int> > rest,b;
int f[MAXN],re = 0,p[MAXN][20];
int ff(int x)
{
if (f[x] == x)
return f[x];
else
f[x] = ff(f[x]);
return f[x];
}
int lca(int x, int y)//倍增求LCA
{
if (dep[x] > dep[y])
swap(x, y);
for (int i = 17; i >= 0; i--)
if (dep[x] <= dep[y] - (pr[i]))
y = p[y][i];
if (x == y)//如果x直接等于y了要直接返回;
return x;
for (int i = 17; i >= 0; i--)
{
if (p[x][i] == p[y][i]) continue;
x = p[x][i], y = p[y][i];
}
return p[x][0];
}
void dfs(int u, int fa)
{
dep[u] = dep[fa] + 1;
p[u][0] = fa;
for (int i = 1; i <= 17; i++)
p[u][i] = p[p[u][i - 1]][i - 1];
int len = a[u].size();
for (int i = 0;i <= len-1;i++)
{
int y = a[u][i];
if (y != fa)
dfs(y, u);
}
}
void spfa(int s)
{
memset(dis[s], 127 / 3, sizeof(dis[s]));
dis[s][c[s]] = 0;
dl.push(c[s]);
while (!dl.empty())
{
int x = dl.front();
dl.pop();
int len = a[x].size();
for (int i = 0; i <= len - 1; i++)
{
int y = a[x][i];
if (dis[s][y] > dis[s][x] + 1)
{
dis[s][y] = dis[s][x] + 1;
dl.push(y);
}
}
}
}
int main()
{
//freopen("F:\\rush.txt", "r", stdin);
scanf("%d%d%d", &n, &m, &q);
for (int i = 1; i <= n; i++)
f[i] = i;
for (int i = 1; i <= m; i++)
{
int x, y;
scanf("%d%d", &x, &y);
if (x == y)
continue;
int r1 = ff(x), r2 = ff(y);
if (r1 != r2)
{
f[r1] = r2;
a[x].push_back(y);
a[y].push_back(x);
}
else
rest.push_back(make_pair(x, y));
}
dfs(1, 0);
pr[0] = 1;
for (int i = 1; i <= 17; i++)
pr[i] = pr[i - 1] << 1;
for (int i = 1; i <= q; i++)
{
int x, y;
scanf("%d%d", &x, &y);
b.push_back(make_pair(x, y));
ans[i] = dep[x] + dep[y] - 2 * dep[lca(x, y)];
}
int len = rest.size();
for (int i = 0; i <= len - 1; i++)
{
int x = rest[i].first, y = rest[i].second;
a[x].push_back(y);
a[y].push_back(x);
c.push_back(x);
c.push_back(y);
}
len = c.size();
for (int i = 0; i <= len - 1; i++)
spfa(i);
for (int i = 0; i <= q-1; i++)
{
int x, y;
x = b[i].first, y = b[i].second;
for (int j = 0; j <= len - 1; j++)
{
int s = j;
ans[i+1] = min(ans[i+1], dis[s][x] + dis[s][y]);
}
printf("%d\n", ans[i+1]);
}
return 0;
}