题意简洁明了, 数据级别,直接排除了多源最短路的算法,值得一提的是,
十分奇特,一棵树的边是
,我们可以将这个图看成一棵树加上100条以内的边的图。
先考虑一棵树,如何求两点距离?我们需要前置知识点:LCA(最近公共祖先)
红色框框的两个节点u,v,求他们的距离,看他们的深度:
depth[u]=4,depth[v]=3,depth[lca]=2
细心观察,可以发现他们的树上的距离公式:
对于求LCA,方法也多种多样,倍增,树剖都是常用的主流方法,因个人喜好,影响不大。
我们解决了这个问题,那么对于多出来的100条边怎么办?(不一定是100条,其实是多出来的边数为m-n+1条,下文为了方便,一律写100条边)
我们思考一个问题:这些多出来的边,到底有没有用?
求u,v最短路的时候,对于多出来的100条边,有两种情况:
1)假设存在多出的一条边 x->y,如果从u到v最短路上,不经过x->y这条边,那么这条边不造成任何影响
2)如果经过了x->y,那么点x和点y一定都是u到v的最短路上的点,一定有 ,y同理。
所以,多出来的100条边,最多只有200个点,然后,我们直接在完整的图上对这200个点进行bfs求单源最短路。
答案已经呼之欲出了,u->v的最短路
但是!!!!!!!!!!你以为这就可以AC了吗?
直接枚举100条边的200个点进行bfs,极大可能会TLE,(啊啊啊啊!!!)
其实这200个点,是不是一定要枚举完?对于一条边x->y,我们需不需要x和y都进行一次bfs,还是只选择x或y其中一个进行bfs即可?答案当然是其中一个bfs即可啦,不然我也不会凑字数写这段话了hhhh(滑稽
为什么枚举一个即可,如果只枚举一个,会不会有错误?
按照正常来说,是不是错误只会发生在这种情况,多出来的是x->u这条边,但是我只对x进行了一次bfs,现在要求u->v的最短路,u到v的最短路等于1,而x到u的最短路+x到v的最短路=3,并不相等,是不是就错误了??
其实并不会错,如果u到v的最短路中,不经过多出来的100条边,那么最短路就是u,v的树上距离,没有影响,如果经过了其中某些边,这些边的两个顶点都一定在这条最短路上。所以,我们只需要枚举这些边的任意一个点即可,这样就能AC了,撒花★,°:.☆( ̄▽ ̄)/$:.°★ 。
#include <cctype>
#include <cfloat>
#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <algorithm>
#include <deque>
#include <fstream>
#include <functional>
#include <iomanip>
#include <iostream>
#include <istream>
#include <iterator>
#include <list>
#include <map>
#include <ostream>
#include <queue>
#include <set>
#include <sstream>
#include <stack>
#include <string>
#include <utility>
#include <vector>
#include <unordered_map>
#include <unordered_set>
#define ll long long
#define pll pair<long long, long long>
#define P pair<int, int>
#define PP pair<P, P>
#define eps 1e-10
#define PI 3.1415926535898
#define It set<node>::iterator
using namespace std;
const int INF=0x3f3f3f3f;
inline int Read() {
int num=0;
char ch=getchar();
while (!isdigit(ch)) ch=getchar();
while (isdigit(ch)) {
num=num*10+ch-'0';
ch=getchar();
}
return num;
}
const int maxn=1e5+150;
struct edge {
int from,to,Next;
}Edge[maxn<<1];
int n,m,Head[maxn],tot,lg[maxn],dp[maxn][23],dep[maxn],dis[210][maxn],idx;
bool vis[maxn<<1],used[maxn<<1],pos[maxn];
void Add(int from, int to) {
Edge[tot]={from,to,Head[from]};
Head[from]=tot++;
Edge[tot]={to,from,Head[to]};
Head[to]=tot++;
}
void dfs(int u, int fa) {
vis[u]=true;
dep[u]=dep[fa]+1;
dp[u][0]=fa;
for (int i=1; i<lg[dep[u]]; i++) dp[u][i]=dp[dp[u][i-1]][i-1];
for (int i=Head[u]; ~i; i=Edge[i].Next) {
int v=Edge[i].to;
if (vis[v]) continue;
used[i]=used[i^1]=true;
dfs(v,u);
}
}
void bfs(int s) {
++idx;
queue<int> Q;
for (int i=0; i<=n; i++) dis[idx][i]=INF,vis[i]=false;
Q.push(s);
dis[idx][s]=0;
while (!Q.empty()) {
int u=Q.front();
Q.pop();
if (vis[u]) continue;
vis[u]=true;
for (int i=Head[u]; ~i; i=Edge[i].Next) {
int v=Edge[i].to;
if (dis[idx][v]>dis[idx][u]+1) {
dis[idx][v]=dis[idx][u]+1;
Q.push(v);
}
}
}
}
int LCA(int a, int b) {
if (dep[a]<dep[b]) swap(a,b);
while (dep[a]>dep[b]) {
a=dp[a][lg[dep[a]-dep[b]]-1];
}
if (a==b) return a;
for (int i=lg[dep[a]]-1; i>=0; --i) {
if (dp[a][i]!=dp[b][i]) {
a=dp[a][i];
b=dp[b][i];
}
}
return dp[a][0];
}
int main() {
n=Read(),m=Read();
Head[0]=-1;
for (int i=1; i<=n; i++) lg[i]=lg[i-1]+(i==(1<<lg[i-1])),Head[i]=-1;
for (int i=1; i<=m; i++) {
int u=Read(),v=Read();
Add(u,v);
}
vis[0]=true;
dfs(1,0);
for (int i=0; i<tot; i+=2) {
if (used[i]) continue;
used[i]=used[i^1]=true;
if (!pos[Edge[i].from]) pos[Edge[i].from]=true,bfs(Edge[i].from);
// if(!pos[Edge[i].to]) pos[Edge[i].to]=true,bfs(Edge[i].to);
}
int q=Read();
while (q--) {
int u=Read(),v=Read();
int res=dep[u]+dep[v]-2*dep[LCA(u,v)];
for (int i=1; i<=idx; i++) res=min(res,dis[i][u]+dis[i][v]);
printf("%d\n",res);
}
return 0;
}


京公网安备 11010502036488号