GFOJ problem468 出去玩 解题报告

题目: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 
#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"%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);
    }
}

你可能感兴趣的:(解题报告,算法,LCA)