GalaxyOJ-468 (LCA)
题目
Problem Description
穷游中国队又去游中国了。
穷游中国队去到了一个城市,这个城市有n个区,n-1条路,每条路连接不同的两个区,而且每两个区有且仅有一条路径,每条路上都有一个收费站,如果经过这条路就要收取一定的费用。
现在穷游中国队在编号为a的区住了一个晚上,第二天打算去编号为b的区,问路费一共要花多少钱?(在一个区里面行走不用收费)
Input
第一行两个数n和q,表示区的个数和询问个数
接下来有n-1行,每行三个数i,j,k,表示第i个区到第j个区有一条路,且要收费k元
接下来有q行,每行两个数,分别是a和b
$ 2<=n<=40000 1<=m<=200 0<k<=40000$
Output
对于每个询问,输出一个整数,表示从a区走到b区要花多少钱。
Sample Input
|#|Input|Output|
|-|-|-|
|1|3 2
1 2 10
3 1 15
1 2
2 3
|10
25|
|2|2 2
1 2 100
1 2
2 1|100
100|
分析
- 从数据范围来看,直接搜索是没前途的,注意到边比点少一个,而且点与点之间没有重复的边,于是这就变成了一棵树,所以,我们想到了LCA。
- 首先随便dfs一下给这棵树的节点编个顺序。
- 不难看出,对于这个要从一个点到另一个点,是一定要经过他们的最近公共祖先的,而且路径就是从这个点到它们的公共祖先,然后再走下去到另一个点,于是就是一个近乎模板的题了。
- 具体操作与LCA几乎相同,用求
fa[][]
相似的方法求出val[i][x]
(节点i跳到 \(2^x\) 代祖先所需的路费),对于每个询问只需加一下对应的路费就行了。
程序
第一次打LCA,程序有点丑……
#include <cstdio>
#define For(x) for (int o=head[x],k=nn[o]; o; o=To[o],k=nn[o])
int A,B,n,q,num,w[80010],head[40010],nn[80010],val[80010][25],To[80010],f[40010],d[40010],fa[40010][25];
void add(){
int u,v,ww; scanf("%d%d%d",&u,&v,&ww);
To[++num]=head[u], head[u]=num, nn[num]=v, w[num]=ww;
To[++num]=head[v], head[v]=num, nn[num]=u, w[num]=ww;
}
void dfs(int x,int dep){
d[x]=f[x]=dep;
For(x) if (!f[k]){
fa[k][0]=x;
val[k][0]=w[o];
dfs(k,dep+1);
}
}
void Get_fa(){
for (int j=1; j<=20; j++) for (int i=1; i<=n; i++){
fa[i][j]=fa[fa[i][j-1]][j-1];
val[i][j]=val[fa[i][j-1]][j-1]+val[i][j-1];
}
}
int F(int x,int y){ //xµÄµÚy´ú×æÏÈ
for (int i=0; y; i++){
if (y&1) x=fa[x][i];
y>>=1;
}
return x;
}
int V(int x,int y){ //xµ½µÚy´ú×æÏȵÄÖµ
int ans=0;
for (int i=0; y; i++) {
if (y&1) ans+=val[x][i],x=fa[x][i];
y>>=1;
}
return ans;
}
int LCA(int x,int y){
for (int i=20; i>=0; i--) if (d[x]-1>=1<<i)
if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
int main(){
scanf("%d%d",&n,&q);
for (int i=1; i<n; i++) add();
fa[1][0]=1; dfs(1,1);
Get_fa();
while (q--){
int Sum=0;
scanf("%d%d",&A,&B);
if (d[A]>d[B]) Sum+=V(A,d[A]-d[B]),A=F(A,d[A]-d[B]);
if (d[B]>d[A]) Sum+=V(B,d[B]-d[A]),B=F(B,d[B]-d[A]);
int kk;
if (A!=B){
kk=LCA(A,B);
Sum+=V(A,d[A]-d[kk])+V(B,d[B]-d[kk]);
}
printf("%d\n",Sum);
}
//for (int i=1; i<=n; i++) printf("[%d]\t%d\t%d\n",i,d[i],fa[i][0]);
}