LCA三种算法,轻重链剖分板子记录

本篇用于给本蒟蒻回忆板子

一.倍增LCA

dfs预处理深度和ST跳跃表,再从高二进制位向低二进制位遍历进行跳跃(与快速幂的思想一致,本质都是一个数能够被转化为二进制)。

先跳到同一层,判断此时是不是LCA,如果不是,再二进制位从高到低遍历,如果遇到跳越后仍然不在同一点的情况,俩个点可以同时直接跳跃;

最后俩个点的父节点是共同的,即为他们的LCA。

ST表的处理原理如下:2i=2i-1+2i-1因此这一块就直接从低到高枚举即可(往上跳的过程均从高到低)

vector<int> dep;
vector<vector<int>> up;
vector<int> e[N];
void dfs(int u,int fa){dep[u]=dep[fa]+1;up[u][0]=fa;for(int i=1;i<=20;i++)up[u][i]=up[up[u][i-1]][i-1];for(auto v:e[u]){if(v==fa)continue;dfs(v,u);}
}
int lca(int x,int y){if(dep[x]<dep[y])swap(x,y);for(int i=20;i>=0;i--)if(dep[up[x][i]]>=dep[y])x=up[x][i];if(x==y)return x;for(int i=20;i>=0;i--){if(up[x][i]!=up[y][i]){x=up[x][i];y=up[y][i];}}return up[x][0];
}

二.Tarjan离线LCA

先自根向下把整棵树打上标记,然后在退出dfs的过程中,自下向上地进行离线处理查询:

从叶子节点开始把每条退出dfs经过的边用并查集合并,这样就可以在子树的范围内就可以把俩个节点的LCA直接处理掉,不会意外得拿更高层级的LCA作为答案。

image

image

DSU dsu(N);
vector<int> e[N];
vector<pair<int,int>> qy[N];
vector<int> ans(N,0);
int vis[N];
void dfs(int u,int fa){vis[u]=1;for(auto v:e[u]){if(!vis[v]){dfs(v,u);dsu.fa[v]=u;}}for(auto [v,i]:qy[u]){if(!vis[v] || ans[i])continue;ans[i]=dsu.find(v);}
}

三.树链剖分LCA

思想可以类比倍增LCA。将树剖分为链之后可以将要查询的u,v俩点跳跃到同一条重链上,然后比较深度来确定谁是最近公共祖先。

对于u,v俩点,while(top[u]!=top[v])时,可以找到深度更深的重链的链头,之后爬到该链头的父节点进入下一条重链,直至u,v俩点在同一重链

第一次dfs完成父节点(fa),子树大小(sz),重儿子(son)的预处理;第二次dfs完成链头数组的处理

int n,m,root;
vector<int> e[N];
int fa[N],dep[N],son[N],sz[N],top[N];
void dfs1(int u,int f){fa[u]=f,dep[u]=dep[f]+1,sz[u]=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;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;
}

四.轻重链剖分

重链剖分部分与3大差不差,在其基础上,我们对每个节点重新按照重链自上向下递归编号;

  1. 这样一条重链的所有编号就是连续的;
  2. 而一个以u为根的子树中的所有节点,也都是在一个连续的区间当中的;

  因为对于一棵子树来说,会先把重链标号,而在重链节点不断向下递归的过程中会访问其他轻儿子节点,进而当子树重新递归回根节点时,整棵子树都已经被完全编号,且编号连续,为一个区间。之后利用重链上新节点是区间连续的,这一特性,我们利用线段树去维护区间信息;又树上的任意一条路径最多被分为logn条重链的部分,只需要对这几条重链的区间分别操作就可以快速维护整条路径的修改或查询。洛谷p3384板子

#include<bits/stdc++.h>
using namespace std;
#define ull unsigned long long
#define lc id<<1
#define rc id<<1|1
const int N=1e5+5;
vector<int> e[N];
int n,m,root,mod,w[N];
int fa[N],son[N],sz[N],dep[N];
int top[N],id[N],nw[N],idx;
void dfs1(int u,int f){fa[u]=f;sz[u]=1;dep[u]=dep[f]+1;for(int 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;id[u]=++idx,nw[idx]=w[u];if(!son[u])return;dfs2(son[u],t);for(int v:e[u]){if(v==fa[u] || v==son[u])continue;dfs2(v,v);}
}struct pi{int l,r;int sum,add;
}tr[N*4];
void pushup(int id){tr[id].sum=(tr[lc].sum+tr[rc].sum)%mod;
}
void pushdown(int id){if(!tr[id].add)return;tr[lc].sum=(tr[lc].sum+tr[id].add*(tr[lc].r-tr[lc].l+1))%mod;tr[rc].sum=(tr[rc].sum+tr[id].add*(tr[rc].r-tr[rc].l+1))%mod;tr[lc].add=(tr[lc].add+tr[id].add)%mod;tr[rc].add=(tr[rc].add+tr[id].add)%mod;tr[id].add=0;
}
void build(int id,int l,int r){tr[id]={l,r,nw[l]%mod,0};if(l>=r)return;int md=(l+r)>>1;build(lc,l,md);build(rc,md+1,r);pushup(id);
}
void chg0(int id,int x,int y,int k){int l=tr[id].l,r=tr[id].r;if(x<=l && r<=y){tr[id].sum=(tr[id].sum+(r-l+1)*k)%mod;tr[id].add=(tr[id].add+k)%mod;return;}pushdown(id);int md=(l+r)>>1;if(x<=md)chg0(lc,x,y,k);if(y>md)chg0(rc,x,y,k);pushup(id);
}
void chg1(int u,int v,int k){while(top[u]!=top[v]){if(dep[top[u]]<dep[top[v]])swap(u,v);chg0(1,id[top[u]],id[u],k);u=fa[top[u]];}if(dep[u]<dep[v])swap(u,v);chg0(1,id[v],id[u],k);
}
void chg3(int u,int k){int l=id[u],r=id[u]+sz[u]-1;chg0(1,l,r,k);
}
int query0(int id,int x,int y){int l=tr[id].l,r=tr[id].r;if(x<=l && r<=y)return tr[id].sum;pushdown(id);int md=(l+r)>>1;int res=0;if(x<=md)res=(res+query0(lc,x,y))%mod;if(y>md)res=(res+query0(rc,x,y))%mod;return res;
}
int query2(int u,int v){int res=0;while(top[u]!=top[v]){if(dep[top[u]]<dep[top[v]])swap(u,v);res=(res+query0(1,id[top[u]],id[u]))%mod;u=fa[top[u]];}if(dep[u]<dep[v])swap(u,v);res=(res+query0(1,id[v],id[u]))%mod;return res;
}
int  query4(int u){int l=id[u],r=id[u]+sz[u]-1;return query0(1,l,r);
}
void solve(){cin>>n>>m>>root>>mod;for(int i=1;i<=n;i++)cin>>w[i];for(int i=1;i<n;i++){int u,v;cin>>u>>v;e[u].push_back(v);e[v].push_back(u);}dfs1(root,0);dfs2(root,root);build(1,1,idx);int op,x,y,z;for(int i=1;i<=m;i++){cin>>op;if(op==1){cin>>x>>y>>z;chg1(x,y,z);}else if(op==2){cin>>x>>y;cout<<query2(x,y)<<'\n';}else if(op==3){cin>>x>>y;chg3(x,y);}else{cin>>x;cout<<query4(x)<<'\n';}}
}
signed main(){ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);int _=1;//cin>>_;while(_--)solve();
}