LCA:最近公共祖先

lca(u,v)表示u与v最近公共祖先

暴力法:O(n)

倍增:f[i][j]代表i的2^j级父亲

f[i][j]=f[f[i][j-1]][j-1]

for(int i = 1;i <= n;i++) f[i][0] = fa[i]//预设:0级父亲就是父亲
for(int j = 1;j <= 20;j++){   /*从1开始跳k步*/
    for(int i = 1;i <= n;i++){
        f[i][j] = f[f[i][j-1]][j-1];
    }
} 

求lca(u,v)?

1.把u和v中较深的上跳|dep[u]-dep[v]|步

如果此时u,v相等那么返回

2.如果此时u和v相等,那么返回,否则

3.i从大到小枚举,判断f[u][i]与f[v][i]是否相同,不同就跳

4.此时答案就是u和v最近公共祖先

int lca(int x,int y){
    if(dep[x]<dep[y]) swap(x,y);
    for(int i = 20;i > 0;i++){
        if(dep[f[x][i]] >= dep[y]) x = f[x][i];
    }
    if(x == y) return x;
    for(int i = 20;i >= 0;i--){
        if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    }
    return f[x][0];
}

 

例题

题目描述

欢乐岛上有个非常好玩的游戏,叫做“紧急集合”。在岛上分散有 nnn 个等待点,有 n−1n-1n−1 条道路连接着它们,每一条道路都连接某两个等待点,且通过这些道路可以走遍所有的等待点,通过道路从一个点到另一个点要花费一个游戏币。

参加游戏的人三人一组,开始的时候,所有人员均任意分散在各个等待点上(每个点同时允许多个人等待),每个人均带有足够多的游戏币(用于支付使用道路的花费)、地图(标明等待点之间道路连接的情况)以及对话机(用于和同组的成员联系)。当集合号吹响后,每组成员之间迅速联系,了解到自己组所有成员所在的等待点后,迅速在 nnn 个等待点中确定一个集结点,组内所有成员将在该集合点集合,集合所用花费最少的组将是游戏的赢家。

小可可和他的朋友邀请你一起参加这个游戏,由你来选择集合点,聪明的你能够完成这个任务,帮助小可可赢得游戏吗?
输入格式

第一行两个正整数 nnn 和 mmm,分别表示等待点的个数(等待点也从 111 到 nnn 进行编号)和获奖所需要完成集合的次数。

随后 n−1n-1n−1 行,每行两个正整数 a,ba,ba,b,表示编号为 aaa 和编号为 bbb 的等待点之间有一条路。

随后 mmm 行,每行用三个正整数 x,y,zx,y,zx,y,z,表示某次集合前小可可、小可可的朋友以及你所在等待点的编号。
输出格式


 

 

 

树上dp:

dep[i] = dep[fa[i]] + w[i]

void dfs(int fa,int x){
    f[x] = v[x];
    for(int i = head[x];i = nxt[i]){
        if(to[i]==fa) continue; //不搜父亲
        dfs(x,to[i]);
        f[x]+=max(f[to[i]],0); 
    }
    ans = max(ans,f[x]);
}

 

posted @ 2022-02-12 11:19  yinfelix  阅读(34)  评论(0)    收藏  举报