最近的一些比赛中经常用到树上的常见模型,整理一下:
首先整理一下的四种求法:
1.倍增:预处理 单次查询
通过预处理节点深度和表倍增处理祖宗节点,在查询的时候用倍增先将两点跳到统一深度,再找同祖宗节点的最深节点
int n,depth[N],f[N][19];
vector<int>e[N];
void bfs(int root){
memset(depth,0x3f,sizeof depth);
depth[0]=0,depth[root]=1;//0为st表的哨兵
queue<int>q;
q.push(root);
while(q.size()){
int ver=q.front();
q.pop();
for(auto &to:e[ver]){
if(depth[to]>depth[ver]+1){
depth[to]=depth[ver]+1;
f[to][0]=ver;
for(int i=1;i<=18;i++)
f[to][i]=f[f[to][i-1]][i-1];
}
}
}
}
int lca(int a,int b){
if(depth[a]<depth[b])swap(a,b);
for(int i=18;i>=0;i--){
if(depth[f[a][i]]<=depth[b])
a=f[a][i];
}
if(a==b)return a;
for(int i=18;i>=0;i--){
if(f[a][i]!=f[b][i])
a=f[a][i],b=f[b][i];
}
return f[a][0];
}
2.离线: 总体时间复杂度 ,为节点 为查询次数
暂定
3.在线:预处理 单次查询
通过处理欧拉序及其深度,并保存每个节点第一次出现位置,通过表维护区间深度最小值即可
int n,q,root,depth[N<<1],f[N<<1][19],se[N<<1],tot,Log[N<<1],id[N];
vector<int>e[N];
void dfs(int u,int d,int fa){
se[++tot]=u;
id[u]=tot;
depth[tot]=d;
for(auto &to:e[u]){
if(to==fa)continue;
dfs(to,d+1,u);
se[++tot]=u;
depth[tot]=d;
}
}
int lca(int l,int r){
int k=Log[r-l+1];
return depth[f[l][k]]<depth[f[r-(1<<k)+1][k]]?
se[f[l][k]]:se[f[r-(1<<k)+1][k]];
}
void solve(){
cin>>n>>q>>root;
for(int i=1;i<n;i++){
int u,v;cin>>u>>v;
e[u].pb(v);
e[v].pb(u);
}
dfs(root,1,0);
Log[1]=0,Log[2]=1;
for(int i=3;i<=tot;i++)
Log[i]=Log[i/2]+1;
for(int i=1;i<=tot;i++)f[i][0]=i;
for(int j=1;j<=Log[tot];j++)
for(int i=1;i+(1<<j)-1<=tot;i++)
if(depth[f[i][j-1]]<depth[f[i+(1<<(j-1))][j-1]])f[i][j]=f[i][j-1];
else f[i][j]=f[i+(1<<(j-1))][j-1];
while(q--){
int u,v;cin>>u>>v;
int l=id[u],r=id[v];
if(l>r)swap(l,r);
}
}
4.树链剖分:预处理 单次查询
暂定
树上的几种常用模型:
1.动态维护树的直径
2.树上路径的交
3.树上路径的并