商务旅行
题目描述 Description
某首都城市的商人要经常到各城镇去做生意,他们按自己的路线去做,目的是为了更好的节约时间。
假设有N个城镇,首都编号为1,商人从首都出发,其他各城镇之间都有道路连接,任意两个城镇之间如果有直连道路,在他们之间行驶需要花费单位时间。该国公路网络发达,从首都出发能到达任意一个城镇,并且公路网络不会存在环。
你的任务是帮助该商人计算一下他的最短旅行时间。
输入描述 Input Description
输入文件中的第一行有一个整数N,1<=n<=30 000,为城镇的数目。下面N-1行,每行由两个整数a 和b (1<=a, b<=n; a<>b)组成,表示城镇a和城镇b有公路连接。在第N+1行为一个整数M,下面的M行,每行有该商人需要顺次经过的各城镇编号。
输出描述 Output Description
在输出文件中输出该商人旅行的最短时间。
样例输入 Sample Input
5
1 2
1 5
3 5
4 5
4
1
3
2
5
样例输出 Sample Output
7
#include<cstdio>
#include<vector>
#include<cmath>
using namespace std;
#define maxn 30009
vector<int>edge[maxn];
int dep[maxn], fa[maxn][20], len[maxn][20];
bool wasfa[maxn];
int ans=0;
void dfs(int cur)
{
int k, i, son;
dep[cur]=dep[fa[cur][0]]+1;
k=ceil(log(dep[cur])/log(2)); //当前节点最多跳几次
for(i=1; i<=k; i++)
{
fa[cur][i]=fa[fa[cur][i-1]][i-1];
len[cur][i]=len[cur][i-1]+len[fa[cur][i-1]][i-1];
}
vector<int>::iterator it;
wasfa[cur]=1;
for(it=edge[cur].begin(); it!=edge[cur].end(); it++)
{
son=(*it);
if(!wasfa[son])
{
wasfa[son]=true;
fa[son][0]=cur; //这里容易漏写,这里不告诉儿子他爹是谁,他就再也不知道了(除非传参)
len[son][0]=1; //相邻点的时间都是1
dfs(son);
}
}
}
void lca(int x, int y)
{
int k, i;
int s;
if(dep[x]<dep[y]) swap(x, y);
k=dep[x]-dep[y];
s=ceil(log(dep[x])/log(2));
for(i=0; i<=s; i++) //这个地方我写错过,写成了从1开始循环,应该从父亲节点开始循环
{
if(k&(1<<i))
{
ans+=len[x][i];
x=fa[x][i];
}
}
if(x==y) return;
s=ceil(log(dep[x])/log(2));
for(i=s; i>=0; i--)
{
if(fa[x][i]!=fa[y][i])
{
ans+=len[x][i];
x=fa[x][i];
ans+=len[y][i];
y=fa[y][i];
}
}
ans+=2;//最后x,y点都在最近公共祖先的儿子位置处,所以要+2
return;
}
int main()
{
int i, a, b, m, to, pre;
int n;
scanf("%d", &n);
for(i=1; i<n; i++)
{
scanf("%d%d", &a, &b);
edge[a].push_back(b);
edge[b].push_back(a);
}
dfs(1);
scanf("%d", &m);
scanf("%d", &pre);
for(i=1; i<m; i++)
{
scanf("%d", &to);
lca(pre, to);
pre=to;
}
printf("%d", ans);
return 0;
}