树
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]); }
浙公网安备 33010602011771号