题目:http://www.gdfzoj.com/oj/contest/105/problems/3
给出一棵小于n个节点的树,和m个询问,对于每个询问,有两个点,求点a到点b的最短距离,其中 n,m < 40000
在这里讲一下倍增LCA算法 虽然说暴力搜索也可以过
f[i][j] 表示 从点i开始,向上2^j个点的位置,由于这道题带权,再用v[i][j] 用相同的方法表示权值,再记录深度d[i]。
于是就有了递推式
f[i][j] = f[f[i][j-1]][j-1];
v[i][j] = v[i][j-1] + v[f[i][j-1]][j-1];
d[i] = d[fa[i][0]] + 1;
对于每次询问,先用 lowbit 使 a ,b 深度相等,
再用二进制枚举使 a,b 有公共祖先的最小向上深度每一位的值。
再说实现,一开始用 vector 记录邻接表 RE,后来手写邻接表AC……
下面放代码:
#include <cstdio>
#define lb(x) (x & (-x))
int n,q,i,j,k,f[50000][20],v[50000][20],d[50000],a,b,dep,ans,head[50000],last[50000],tot;
struct E {int t,v,next;} e[100000];
void dfs(int u,int l) {
d[u] = d[l] + 1;
for (int i=head[u];i;i=e[i].next) if (e[i].t == l) {
f[u][0] = l;
v[u][0] = e[i].v;
for (j=1;j<18;j++) {
f[u][j] = f[f[u][j-1]][j-1];
v[u][j] = v[u][j-1] + v[f[u][j-1]][j-1];
}
} for (int i=head[u];i;i=e[i].next) if (e[i].t != l) dfs(e[i].t,u);
}
int main() {
scanf("%d%d",&n,&q);
for (i=1;i<n;i++) {
scanf("%d%d%d",&a,&b,&k);
e[++tot].t = b; e[tot].v = k;
last[a] = last[a] ? e[last[a]].next = tot : head[a] = tot;
e[++tot].t = a; e[tot].v = k;
last[b] = last[b] ? e[last[b]].next = tot : head[b] = tot;
}
for (dfs(1,0);q--;ans = 0) {
scanf("%d%d",&a,&b);
if (d[a] > d[b]) {int t=a; a=b; b=t;}
for (dep = d[b] - d[a];dep;dep -= lb(dep)) {
for (i=0;i<18;i++) if ((1 << i) == lb(dep)) break;
ans += v[b][i];
b = f[b][i];
}
for (i=17;i>=0;i--) {
if (f[a][i] != f[b][i]) {
ans += v[a][i] + v[b][i];
a = f[a][i];
b = f[b][i];
}
}
if (a != b) {
ans += v[a][0] + v[b][0];
a = f[a][0];
b = f[b][0];
} printf("%d\n",ans);
}
}