GFOJ problem468 出去玩 解题报告

来源:互联网 发布:java 图片识别数字 编辑:程序博客网 时间:2024/04/30 10:57

题目: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);    }}
0 0
原创粉丝点击