算法
倍增法求公共祖先-(m+n)logn
tarjan算法(离线算法)-n+m
树链剖分-n+mlogn
总结
注意:祖宗节点的父节点最好传入0,如果传入-1,则在遍历子节点的时候更新fa和dep数组。
应用
P3379
倍增法:
//倍增
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
int dep[N],fa[N][22];
int n,m,s;
vector<int> e[N];
void dfs(int u,int f) {
dep[u]=dep[f]+1,fa[u][0]=f;
for(int i=1; i<=21; i++) {
fa[u][i]=fa[fa[u][i-1]][i-1];
}
for(auto x:e[u]) {
if(x==f) continue;
dfs(x,u);
}
}
int lca(int u,int v) {
if(dep[u]<dep[v]) swap(u,v);
for(int i=21;i>=0; i--) {
if(dep[fa[u][i]]>=dep[v])
u=fa[u][i];
}
if(u==v) return u;
for(int i=21; i>=0; i--) {
if(fa[u][i]!=fa[v][i])
u=fa[u][i],v=fa[v][i];
}
return fa[u][0];
}
int main() {
ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
cin>>n>>m>>s;
for(int i=0;i<n-1;i++){
int a,b;cin>>a>>b;
e[a].push_back(b);
e[b].push_back(a);
}
dfs(s,0);
while(m--){
int u,v;cin>>u>>v;
int t=lca(u,v);
cout<<t<<endl;
}
// scanf("%d%d%d",&n,&m,&s);
// int a,b;
// for(int i=1; i<n; i++) {
// scanf("%d%d",&a,&b);
// e[a].push_back(b);
// e[b].push_back(a);
// }
// dfs(s,0);
// while(m--)
// scanf("%d%d",&a,&b),printf("%d\n",lca(a,b));
return 0;
}
tarjan算法
//tarjan
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
vector<int> e[N];
typedef pair<int,int> pii;
vector<pii> query[N];
int ans[N*2],vis[N],fa[N];
int find(int x){
if(x==fa[x]) return x;
return fa[x]=find(fa[x]);
}
void tarjan(int u){
vis[u]=1;
for(auto x:e[u]){
if(vis[x]) continue;
tarjan(x);
fa[x]=u;
}
for(auto t:query[u]){
int v=t.first,i=t.second;
if(vis[v]){
ans[i]=find(v);//v的祖宗节点
}
}
}
int main(){
int n,m,s,a,b,x,y;
cin>>n>>m>>s;
for(int i=1;i<n;i++){
cin>>x>>y;
e[x].push_back(y);
e[y].push_back(x);
}
for(int i=1;i<=m;i++){
cin>>a>>b;
query[a].push_back({b,i});
query[b].push_back({a,i});
// query[a].emplace_back(b,i);
// query[b].emplace_back(a,i);
}
for(int i=1;i<N;i++) fa[i]=i;//并查集注意初始化
tarjan(s);
for(int i=1;i<=m;i++){
cout<<ans[i]<<endl;
}
return 0;
}
树剖
//树剖
#include<bits/stdc++.h>
using namespace std;
const int N=5e5+10;
vector<int> e[N];
int fa[N],son[N],top[N],dep[N],sz[N];
void dfs1(int u,int f){
fa[u]=f,sz[u]=1,dep[u]=dep[f]+1;
for(auto v:e[u]){
if(v==f) continue;
dfs1(v,u);
sz[u]+=sz[v];
if(sz[son[u]]<sz[v]) son[u]=v;
}
}
void dfs2(int u,int t){
top[u]=t;
if(!son[u]) return;
dfs2(son[u],t);
for(auto v:e[u]){
if(v==fa[u]||v==son[u]) continue;//v不是父亲节点和重节点
dfs2(v,v);
}
}
int lca(int u,int v){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);//这里比较的是重链头深度
u=fa[top[u]];
}
return dep[u]<dep[v]?u:v;
}
int main(){
int n,m,s,a,b;
cin>>n>>m>>s;
for(int i=1;i<n;i++){
cin>>a>>b;
e[a].push_back(b);
e[b].push_back(a);
}
dfs1(s,0),dfs2(s,s);
while(m--){
cin>>a>>b;
cout<<lca(a,b)<<endl;
}
return 0;
}
景区导游(蓝桥杯真题)
LCA和后缀和
#include<bits/stdc++.h>
#define debug(x) cout<<#x<<" = "<<x<<endl
// #define int long long
using namespace std;
const int N=1e5+10;
vector<pair<int,int>> e[N];
typedef long long ll;
ll fa[N],son[N],sz[N],dep[N],top[N],sum[N];
ll a[N],suf[N];
void dfs1(int u,int f,ll val){
fa[u]=f,dep[u]=dep[f]+1,sz[u]=1;
sum[u]=val;
for(auto x:e[u]){
auto v=x.first,t=x.second;
if(v==f) continue;
dfs1(v,u,val+t);
sz[u]+=sz[v];
if(sz[son[u]]<sz[v]) son[u]=v;
}
}
void dfs2(int u,int t){
top[u]=t;
if(!son[u]) return;
dfs2(son[u],t);
for(auto x:e[u]){
int v=x.first;
if(v==fa[u]||v==son[u]) continue;//fa[u]和son[u]
dfs2(v,v);
}
}
int lca(int u,int v){
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
u=fa[top[u]];
}
return dep[u]<dep[v]?u:v;
}
ll get(int u,int v){
if(u==0||v==0) return 0;
return sum[u]+sum[v]-2*sum[lca(u,v)];
}
signed main(){
int n,k,b,c,x,y;
cin>>n>>k;
for(int i=1;i<n;i++){
cin>>x>>y>>c;
e[x].push_back({y,c});
e[y].push_back({x,c});
}
dfs1(1,0,0);
dfs2(1,1);
for(int i=1;i<=k;i++) cin>>a[i];
for(int i=k;i>=1;i--) suf[i]=suf[i+1]+get(a[i],a[i+1]);//减减
ll pre=0;
for(int i=1;i<=k;i++){
cout<<pre+get(a[i-1],a[i+1])+suf[i+1]<<" ";
pre+=get(a[i-1],a[i]);
}
return 0;
}