CF1706E 题解
看到询问,我们想到Kruskal重构树。
简单来说,按照输入顺序,建立Kruskal重构树,第 \(i\) 个询问中,对 \([l_i,r_i]\) 求 LCA , LCA 的权值就是答案。
现在来讨论,如何求一个范围内点的 LCA (重点)
首先来证明: \(lca_{x,x-1},lca_{x,x+1}\) 中较浅的是 \(lca_{x-1,x,x+1}\)
显然,前两个 LCA 都是 \(x\) 的祖先,因此两 LCA 存在祖孙关系。
因此,较浅的为三个点的 LCA
很容易将其拓展至多个点的情况: \(lca_{l,l+1},lca_{l+1,l+2},lca_{l+2,l+3}\cdots lca_{r-1,r}\) 中最浅的为 \(lca_{l,l+1,l+2\cdots r}\)
而相邻两个点的 \(lca\) 可以预处理,用 ST 表维护,询问时直接在 ST 表中查询即可。
若看不懂,可结合代码食用:
#include<bits/stdc++.h>
using namespace std;
#define N 100001
int T,n,m,q,bcj[N<<2],val[N<<2],num,tot,last[N<<2],fa[21][N<<2],dep[N<<2];
int lg[N],st[21][N];
struct edge{
int to,pre;
}e[N<<3];
int get(int x){
return bcj[x]==x?x:bcj[x]=get(bcj[x]);
}
void add(int x,int y){
e[++tot].to=y;e[tot].pre=last[x];last[x]=tot;
}
void dfs(int x,int f){
fa[0][x]=f;dep[x]=dep[f]+1;
for(int i=1;i<=20;++i)fa[i][x]=fa[i-1][fa[i-1][x]];
for(int i=last[x];i;i=e[i].pre){
int y=e[i].to;if(y==f)continue;
dfs(y,x);
}
}
int lca(int x,int y){
if(dep[x]<dep[y])swap(x,y);
for(int i=20;i>=0;--i){
if(dep[fa[i][x]]>=dep[y])x=fa[i][x];
}
if(x==y)return x;
for(int i=20;i>=0;--i){
if(fa[i][x]!=fa[i][y])x=fa[i][x],y=fa[i][y];
}
return fa[0][x];
}
int query(int l,int r){
if(l>r)return 0;
int k=lg[r-l+1];
return dep[st[k][l]]>dep[st[k][r-(1<<k)+1]]?st[k][r-(1<<k)+1]:st[k][l];
}
int main(){
scanf("%d",&T);
while(T--){
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=n+m;++i)bcj[i]=i,last[i]=val[i]=0;tot=0;num=n;
for(int i=1;i<=m;++i){
int x,y;scanf("%d%d",&x,&y);
if(get(x)==get(y))continue;
int z=++num;val[z]=i;
add(z,get(x));add(z,get(y));bcj[get(x)]=bcj[get(y)]=z;
}
dfs(num,0);lg[0]=-1;
for(int i=1;i<n;++i){
st[0][i]=lca(i,i+1);lg[i]=lg[i>>1]+1;
}
for(int i=1;i<=20;++i){
for(int j=1;j+(1<<i)<=n;++j){
st[i][j]=dep[st[i-1][j]]>dep[st[i-1][j+(1<<i-1)]]?st[i-1][j+(1<<i-1)]:st[i-1][j];
}
}
while(q--){
int l,r;scanf("%d%d",&l,&r);
printf("%d ",val[query(l,r-1)]);
}
}
return 0;
}

浙公网安备 33010602011771号