题意:
有一颗n个节点的树,经过一条边消耗一点体力。有两个特殊点之间有一个观光缆车,他们之间不需要消耗体力。有Q个询问,每个询问求从x点到y点消耗体力值最少为多少?
思路:
求任意两点树上的距离应该用LCA.
由于多了个电缆,所以我们从x到y是有3种方案:
①从x直接到y,不坐电缆。
②从x到u,再从u坐电缆到v,最后从v到y。
③从x到v,再从v坐电缆到u,最后从u到y。
我们取这三种方案的最少值就可以了
注意:
lca写的丑的会被卡时间,由于u,v固定,所以可以用dfs先求出树上每一个节点到u,v的距离进行优化。
代码:
#include <bits/stdc++.h>
typedef long long ll;
using namespace std;
const ll inf=998244353;
inline int read()
{
int x=0, y=1;
char c=getchar();
while(c<'0'||c>'9')
{
if(c=='-')
{
y=-y;
}
c=getchar();
}
while(c>='0'&&c<='9')
{
x=(x<<1)+(x<<3)+(c^48);
c=getchar();
}
return x*y;
}
vector<int> g[300005];
int dep[300005], parent[20][300005];
int n;
void dfs(int v,int f,int d)
{
dep[v]=d;
parent[0][v]=f;
for(int k=0;(1<<(k+1))<=dep[v];k++)
{
if(parent[k][v]<0)
{
parent[k+1][v]=-1;
break;
}
else
{
parent[k+1][v]=parent[k][parent[k][v]];
}
}
for(int i=0;i<g[v].size();i++)
{
if(g[v][i]!=f)
{
dfs(g[v][i],v,d+1);
}
}
}
int d1[300005], d2[300005];
void dfs1(int v,int f,int d)
{
d1[v]=d;
for(int i=0;i<g[v].size();i++)
{
if(g[v][i]!=f)
{
dfs1(g[v][i],v,d+1);
}
}
}
void dfs2(int v,int f,int d)
{
d2[v]=d;
for(int i=0;i<g[v].size();i++)
{
if(g[v][i]!=f)
{
dfs2(g[v][i],v,d+1);
}
}
}
void inti()
{
memset(parent,-1,sizeof(parent));
dfs(1,-1,0);
}
int lca(int u,int v)
{
if(dep[u]<dep[v])
{
swap(u,v);
}
int s=(int)log2(n);
for(int k=s;k>=0;k--)
{
if((dep[u]-dep[v])>=(1<<k))
{
u=parent[k][u];
}
}
if(u==v)
{
return u;
}
for(int k=s;k>=0;k--)
{
if(parent[k][v]!=parent[k][u])
{
u=parent[k][u];
v=parent[k][v];
}
}
return parent[0][u];
}
int dis(int x, int y)
{
return dep[x]+dep[y]-2*dep[lca(x,y)];
}
int main()
{
n=read();
for(int i=1;i<n;i++)
{
int u=read(), v=read();
g[u].push_back(v);
g[v].push_back(u);
}
inti();
int u, v;
u=read();
v=read();
dfs1(u,-1,0);
dfs2(v,-1,0);
int q=read();
while(q--)
{
int x=read(), y=read();
printf("%d\n",min(dis(x,y),min(d1[x]+d2[y],d1[y]+d2[x])));
}
return 0;
}
京公网安备 11010502036488号