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]);
}
posted @ 2017-04-09 18:03  Jacky#50  阅读(93)  评论(0编辑  收藏  举报