BZOJ 1602: [Usaco2008 Oct]牧场行走【LCA】
1602: [Usaco2008 Oct]牧场行走
Time Limit: 5 Sec Memory Limit: 64 MB
Description
N头牛(2<=n<=1000)别人被标记为1到n,在同样被标记1到n的n块土地上吃草,第i头牛在第i块牧场吃草。 这n块土地被n-1条边连接。 奶牛可以在边上行走,第i条边连接第Ai,Bi块牧场,第i条边的长度是Li(1<=Li<=10000)。 这些边被安排成任意两头奶牛都可以通过这些边到达的情况,所以说这是一棵树。 这些奶牛是非常喜欢交际的,经常会去互相访问,他们想让你去帮助他们计算Q(1<=q<=1000)对奶牛之间的距离。
Input
- 第一行:两个被空格隔开的整数:N和Q
- 第二行到第n行:第i+1行有两个被空格隔开的整数:AI,BI,LI
- 第n+1行到n+Q行:每一行有两个空格隔开的整数:P1,P2,表示两头奶牛的编号。
Output
- 第1行到第Q行:每行输出一个数,表示那两头奶牛之间的距离。
Sample Input
4 2
2 1 2
4 3 2
1 4 3
1 2
3 2
Sample Output
2
7
题解
这题最笨的想法都能过,但是我写了个LCA,求P1,P2的最近公共祖先,然后就前缀和记一下就可了。
代码如下
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
int n,Q,Dep[1005],fa[1005][30],Sum[1005];
int lnk[1005],W[2005],son[2005],nxt[2005],tot;
void Add(int x,int y,int w){son[++tot]=y;W[tot]=w;nxt[tot]=lnk[x];lnk[x]=tot;}
void DFS(int x,int f,int D){
fa[x][0]=f;Dep[x]=D;
for(int j=lnk[x];j;j=nxt[j])
if(son[j]^f) Sum[son[j]]=Sum[x]+W[j],DFS(son[j],x,D+1);
}
void Updata(){
for(int j=1;(1<<j)<n;j++)
for(int i=1;i<=n;i++) fa[i][j]=-1;
for(int j=1;(1<<j)<n;j++)
for(int i=1,t;i<=n;i++)
if(fa[i][j-1]^-1) t=fa[i][j-1],fa[i][j]=fa[t][j-1];
}
int Get(int p,int q){
if(Dep[q]>Dep[p]) swap(p,q);
int Log=0;
for(;(1<<Log)<Dep[p];Log++);Log--;
for(int j=Log;j>=0;j--)
if(fa[p][j]^-1&&Dep[fa[p][j]]>=Dep[q]) p=fa[p][j];
if(p==q) return p;
for(int j=Log;j>=0;j--)
if(fa[p][j]^-1&&fa[p][j]^fa[q][j]) p=fa[p][j],q=fa[q][j];
return fa[p][0];
}
int main(){
#ifndef ONLINE_JUDGE
freopen("prob.in","r",stdin);
freopen("prob.out","w",stdout);
#endif
scanf("%d%d",&n,&Q);
for(int i=1;i<n;i++){
int x,y,w;scanf("%d%d%d",&x,&y,&w);
Add(x,y,w);Add(y,x,w);
}
DFS(1,0,0);Updata();
for(int i=1;i<=Q;i++){
int x,y;scanf("%d%d",&x,&y);
int Root=Get(x,y);
printf("%d\n",Sum[x]+Sum[y]-2*Sum[Root]);
}
return 0;
}