题意:
就是给你n个点,然后n-1条边边权都为1,然后还有一条边但是这条边的边权是0。问你m次两个点的距离最小是多少。
思考:
刚开始我看了看,跑m次最短路?应该会超时的,但是我没别的想法了,看了眼题解。原来是先求lca,那么a和b的最短距离就dep[a]+dep[b]-2*dep[lca(a,b)]。但是还有一个边权是0的边怎么办呢,因为只有这一条是为0的那么完全可以从边权为0的一个端点跑一次最短路,那么a和b的距离就是dist[a]+dist[b]。那么整个复杂度就降下来了。其实好久不用lca对于lca求两点间的最短距离这个想法我已经淡忘了,其实这题也提示你了,为什么给你n-1条边,目的就是提示你这是一个树,而不是图。当然对于0的那条边,还可以这样处理,既然这条边是0,那么不妨求一下lca(a,A)+lca(b,B)或者lca(a,B),lca(b,A)。也就是如果这条边在a和b的最短路径上面那么这样可以求的最小值,如果不在那么就无关。
代码:
int T,n,m,k;
int A,B;
int va[N];
int dist[N],vis[N];
int acc[N][25],dep[N],cnt=22;
vector<PII > e[N];
void dfs(int now,int p)
{
acc[now][0] = p;
dep[now] = dep[p]+1;
for(auto t:e[now])
{
int spot = t.fi;
if(spot==p) continue;
dfs(spot,now);
}
}
int lca(int a,int b)
{
if(dep[a]<dep[b]) swap(a,b);
for(int i=cnt;i>=0;i--)
{
if(dep[acc[a][i]]>=dep[b]) a = acc[a][i];
}
if(a==b) return a;
for(int i=cnt;i>=0;i--)
{
if(acc[a][i]!=acc[b][i])
{
a = acc[a][i];
b = acc[b][i];
}
}
return acc[a][0];
}
void spfa()
{
for(int i=1;i<=n;i++) dist[i] = inf;
queue<int > q;
q.push(A);
dist[A] = 0;
vis[A] = 1;
while(q.size())
{
auto now = q.front();
q.pop();
vis[now] = 0;
for(auto t:e[now])
{
int spot = t.fi,w = t.se;
if(dist[spot]>dist[now]+w)
{
dist[spot] = dist[now]+w;
if(!vis[spot])
{
vis[spot] = 1;
q.push(spot);
}
}
}
}
}
signed main()
{
IOS;
cin>>n;
for(int i=1;i<n;i++)
{
int a,b;
cin>>a>>b;
e[a].pb({b,1});
e[b].pb({a,1});
}
dfs(1,0);
for(int i=1;i<=cnt;i++)
{
for(int j=1;j<=n;j++)
acc[j][i] = acc[acc[j][i-1]][i-1];
}
cin>>A>>B;
e[A].pb({B,0});
e[B].pb({A,0});
spfa();
cin>>m;
while(m--)
{
int a,b;
cin>>a>>b;
cout<<min(dep[a]+dep[b]-2*dep[lca(a,b)],dist[a]+dist[b])<<"\n";
}
return 0;
/* 直接判断A和B这条边是否在a和b的最短里就行
cin>>A>>B;
cin>>m;
while(m--)
{
int a,b;
cin>>a>>b;
cout<<min(get(a,b),min(get(a,A)+get(b,B),get(a,B)+get(b,A)))<<"\n";
}
return 0;
*/
}
总结:
多想想以前的算法,把各种知识结合起来。