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;
}
posted @ 2022-07-19 13:05  Andy__Lin  阅读(93)  评论(0)    收藏  举报