LCA & 用到LCA的各种题目

LCA

倍增实现快速求出树上两点的公共祖先,实现原理像是树上ST表,先预处理出来每个点的2^n的父亲是谁与它的深度,然后求lca时把两点先都跳到同一高度,如果重合为一个点,则这个点就是最近公共祖先,不然就倍增从大到小跳,直到跳到祖先的儿子节点,然后返回此时节点的父亲节点。
代码:

#include <bits/stdc++.h>
using namespace std;
const int N = 5e5+7;
int n,m,s,head[N],f[N][22],dep[N],cnt = 1;
struct Edge{int t,len,nxt;};
Edge edge[N*2];
void AddEdge(int f,int t) //前向星建图,洛谷模板卡vector
{
	edge[cnt].t = t;
	edge[cnt].nxt = head[f];
	head[f] = cnt;
	cnt++;
}
void DFS(int x, int fa) //dfs预处理
{
	f[x][0] = fa;
	dep[x] = dep[fa]+1;
	for(int i = 1; (1<<i) <= dep[x]; i++)
		f[x][i] = f[f[x][i-1]][i-1];
	for(int i = head[x]; i; i = edge[i].nxt)
	{
		int v = edge[i].t;
		if(v == fa) continue;
		DFS(v, x);
	}
}
int LCA(int x, int y) //实现很简单
{
	if(dep[x] > dep[y]) // y >= x
		swap(x,y); 
	for(int i = 20; i >= 0; i--)
		if(dep[x] <= dep[y] - (1<<i))
			y = f[y][i];
	if(x == y) return x;
	for(int i = 20; i >= 0; i--)
		if(f[x][i] != f[y][i])
			x = f[x][i], y = f[y][i];
	return f[x][0];
}  
int main()
{
	scanf("%d %d %d",&n,&m,&s);
	for(int t1,t2,i = 1; i < n; i++)
	{
		scanf("%d %d",&t1,&t2);
		AddEdge(t1,t2);
		AddEdge(t2,t1);
	}
	DFS(s,0);
	for(int t1,t2,i = 1; i <= m; i++)
	{
		scanf("%d %d",&t1,&t2);
		printf("%d\n",LCA(t1,t2));
	}
	return 0;
}

题目

模板题

洛谷P3379 LCA模板

不需要LCA的题

洛谷P2420 异或
因为根据异或的性质,重复异或两次 = 0,所以直接求两点到根节点的异或和再异或即可,不需要求到lca,

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5+7;
int n,m,head[N],vis[N],cnt = 1,dis[N];
struct Edge
{
	int t,val,nxt;
};
Edge edge[N*4];
void AddEdge(int f, int t, int val)
{
	edge[cnt].t = t;
	edge[cnt].val = val;
	edge[cnt].nxt = head[f];
	head[f] = cnt;
	cnt++;
}
void Dfs(int v, int fa, int val)
{
	dis[v] = val;
	for(int i = head[v]; i; i = edge[i].nxt)
	{
		int u = edge[i].t;
		if(u != fa)
		{
			Dfs(u, v, val ^ edge[i].val);
		}
	}
}
int main()
{
	scanf("%d",&n);
	for(int t1,t2,t3,i = 1; i < n; i++)
	{
		scanf("%d %d %d",&t1,&t2,&t3);
		AddEdge(t1, t2, t3);
		AddEdge(t2, t1, t3);
	}
	Dfs(1, 0, 0);
	scanf("%d",&m);
	for(int t1,t2,i = 1; i <= m; i++)
	{
		scanf("%d %d",&t1,&t2);
		printf("%d\n",dis[t1] ^ dis[t2]);
	}
	return 0;
}
LCA+最大(小)生成树

洛谷P1967 火车运输
因为不是一棵树,但是要求最大边权值,当然是要先跑一遍最大生成树,因为是两个图,建议写结构体存图。

#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5+7;
const int INF = 0x3f3f3f3f;
struct Edge1{int x,y,dis;}edge1[MAXN];
struct Edge2{int to,next,w;}edge2[MAXN];
int cnt,n,m,head[MAXN],deep[MAXN],f[MAXN],fa[MAXN][21],w[MAXN][21];
bool vis[MAXN]; 
void addedge(int from, int to, int w)
{
    edge2[++cnt].next=head[from];
    edge2[cnt].to=to;
    edge2[cnt].w=w;
    head[from]=cnt;
    return ;
}
bool CMP(Edge1 x, Edge1 y)
{
    return x.dis>y.dis;
}
int find(int x)
{ 
    if(f[x]!=x) f[x]=find(f[x]);
    return f[x];
}
void kruskal()
{
    sort(edge1+1, edge1+m+1, CMP); 
    for(int i=1; i<=n; i++)
        f[i]=i;
    for(int i=1; i<=m; i++)
        if(find(edge1[i].x)!=find(edge1[i].y))
		{
            f[find(edge1[i].x)]=find(edge1[i].y);
            addedge(edge1[i].x, edge1[i].y, edge1[i].dis);
            addedge(edge1[i].y, edge1[i].x, edge1[i].dis);
        }
    return ;
}

void dfs(int node)
{
    vis[node]=true;
    for(int i=head[node]; i; i=edge2[i].next)
	{
        int to=edge2[i].to;
        if(vis[to]) continue;
        deep[to]=deep[node]+1; 
        fa[to][0]=node; 
        w[to][0]=edge2[i].w;
        dfs(to);
    }
    return ;
}

int lca(int x, int y)
{
    if(find(x)!=find(y)) return -1; 
    int ans=INF;
    if(deep[x]>deep[y]) swap(x,y);
    for(int i=20; i>=0; i--)
        if(deep[fa[y][i]]>=deep[x])
		{
            ans=min(ans, w[y][i]);
            y=fa[y][i];
        }
    if(x==y) return ans;
    for(int i=20; i>=0; i--)
        if(fa[x][i]!=fa[y][i])
		{
            ans=min(ans, min(w[x][i], w[y][i])); 
            x=fa[x][i]; 
            y=fa[y][i];
        }
    ans=min(ans, min(w[x][0], w[y][0]));
    return ans;
}
int main()
{
    int x,y,z,q;
    scanf("%d%d",&n,&m);
    for(int i=1; i<=m; i++)
	{
        scanf("%d%d%d",&x,&y,&z);
        edge1[i].x=x;
        edge1[i].y=y;
        edge1[i].dis=z;
    } 
    kruskal();
    for(int i=1; i<=n; i++)
        if(!vis[i])
		{
            deep[i]=1; 
            dfs(i);
            fa[i][0]=i;
            w[i][0]=INF;
        }
    for(int i=1; i<=20; i++)
        for(int j=1; j<=n; j++)
		{
            fa[j][i]=fa[fa[j][i-1]][i-1]; 
            w[j][i]=min(w[j][i-1], w[fa[j][i-1]][i-1]);
        }
    scanf("%d",&q);
    for(int i=1; i<=q; i++)
	{
        scanf("%d%d",&x,&y);
        printf("%d\n",lca(x,y));
    }
    return 0;
} 
LCA+树上差分

洛谷P3128 最大流
用树上差分,树上点差分,即每个点的真实值是所有儿子节点更新后的值即儿子节点真实值加上自己的差分值。在树上一段路径每个点权值+1即路径左右两端差分值+1,因为求到lca时lca+2了,但是lca也在路径上,所以要减去一个1,然后在lca的父亲节点-1。

具体是这样的:假设一条u到v的路径,那么这条路径是u—>lca(u,v)—>v的,所以我们把u—>lca(u,v)与lca(u,v)—>v两条路径各自加一,也就是++power[u],++power[v],power[lca(u,v)]-=2
但是这样一来,lca(u,v)上+2又-2等于0,也就是u—>v整条路经上除了lca(u,v)都加了1,为了排除这个干扰,我们把power[lca(u,v)]-=2改成- -power[lca(u,v)],- -power[lca(u,v)的父亲]
LCA用倍增比较方便,最后遍历整棵树统计和

#include <bits/stdc++.h>
using namespace std;
const int N = 5e4+7;
int n,k,head[N],cnt,f[N][22],w[N],dep[N],sum[N],ans;
struct Edge
{
	int f, t, vis, nxt;
};
Edge e[N<<2];
void AddEdge(int _f, int _t)
{
	e[++cnt].t = _t;
	e[cnt].nxt = head[_f];
	head[_f] = cnt;
}
void DFS(int u, int fa)
{
	f[u][0] = fa;
	dep[u] = dep[fa] + 1;
	for(int i = 0; f[u][i]; i++)
		f[u][i+1] = f[f[u][i]][i];
	for(int i = head[u]; i; i = e[i].nxt)
	{
		int v = e[i].t;
		if(v != fa)
			DFS(v, u);
	}
}
int LCA(int x, int y)
{
	//注意顺序!! 这样写的一定要从20->0不然会跳不必要的二进制小位数 
	if(dep[x] < dep[y]) //dep[x] >= dep[y]
		swap(x, y);
	for(int i = 20; i >= 0; i--)
	{
		if(dep[x] - (1<<i) >= dep[y])
			x = f[x][i];
	}
	if(x == y) return x;
	for(int i = 20; i >= 0; i--)
	{
		if(f[x][i] != f[y][i])
			x = f[x][i], y = f[y][i];
	}
	return f[x][0];
}
void GetAns(int u, int fa)
{
	for(int i = head[u]; i; i = e[i].nxt)
	{
		int v = e[i].t;
		if(fa == v) continue;
		GetAns(v, u);
		w[u] += w[v];
		ans = max(ans, w[u]);
	}
}
int main()
{
	scanf("%d %d",&n,&k);
	for(int t1,t2,i = 1; i < n; i++)
	{
		scanf("%d %d",&t1,&t2);
		AddEdge(t1, t2);
		AddEdge(t2, t1);
	}
	DFS(1, 0);
	for(int t1,t2,i = 1; i <= k; i++)
	{
		scanf("%d %d",&t1,&t2);
		int lca = LCA(t1, t2); 
		w[t1]++,w[t2]++,w[lca]--,w[f[lca][0]]--;
	}
	GetAns(1, 0);
	printf("%d\n",ans);
	return 0;
}
  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值