图论——最近公共祖先(LCA)

算法

倍增法求公共祖先-(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;
}
  • 5
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值