LCA——笔记

题目——最近公共祖先

Tagjan

将所有的查询存起来

然后一遍dfs,得出所有LCA

非常奇妙

主要依据于以下操作
13 和 14 的 LCA 是7

当dfs到7时 \(\color{pink}{模拟}\) 断开 3 - 7

然后搜左子树

vis[13] = 1

再搜右子树时

搜到14 并从此继续搜索 回溯后检查发现vis[13] = 1 update答案 

也可以说

最近公共LCA以下 两结点不在同一子树


#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
 
inline void Read(int &x)
{
    x = 0;
    char a = getchar();
    bool f = 0;
    while(a < '0'||a > '9') {if(a == '-') f = 1;a = getchar();}
    while(a >= '0'&&a <= '9') {x = x * 10 + a - '0';a = getchar();}
    if(f) x *= -1;
}
const int MAXN = 500001;
vector<int> G[MAXN];
vector<pair<int,int> > Ask[500001];
//存询问
int f[MAXN],ans[MAXN];
/*
f用并查集来维护加删边
ans用于统一输出
*/
bool vis[MAXN],Get[MAXN];
inline int Find_set(int x)
{
    if(f[x] == x) return x;
    return f[x] = Find_set(f[x]);
}
inline void Union(int u,int v)
{
	int A1 = Find_set(u),A2 = Find_set(v);
	if(A1 != A2) f[A1] = A2;
}
//并查集的标准操作
inline void Tagjan(int x)
{
    int i;
    for(i = 0;i < G[x].size();i++)
    {
        if(!vis[G[x][i]])
        {
            vis[G[x][i]] = 1;
            Tagjan(G[x][i]);
            Union(G[x][i],x);
            /*先Tagjan下去,再连边
        	因为要模拟断边
        	*/
        }
    }
    //遍历整个子树
    for(i = 0;i < Ask[x].size();i++)
    {
        int v = Ask[x][i].first,Index = Ask[x][i].second;
        if(vis[v]) ans[Index] = Find_set(v);
    	/*因为u已经遍历到了
    	v在其他子树的话,上层断边,确保ans
    	v就在此子树的话,u已断边,v必搜到u
    	*/
    }
}
int main()
{
    int i,n,m,s;
    Read(n),Read(m),Read(s);
    for(i = 1;i < n;i++)
    {
    	f[i] = i;
    	int u,v;
    	Read(u),Read(v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    for(i = 1;i <= m;i++)
    {
    	int u,v;
    	Read(u),Read(v);
    	Ask[u].push_back(make_pair(v,i));
    	Ask[v].push_back(make_pair(u,i));
    	//两边都要push进去,因为不清楚谁更浅
    }
    vis[s] = 1;
    //初始一定要清!!!!!
	Tagjan(s);
    for(i = 1;i <= m;i++)
        printf("%d\n",ans[i]);
    return 0;
}

Another Way

线段树维护,又依据于以下原则

有一种叫dfs序的东西 把上图的4,3,7,5看成一棵树(to be convenient

dfs序为3437353

那任两结点的公共祖先就在上序的两对应值间

如7和4 ->3

但这还不足以用线段树来维护

就新增数组

dep[i] 记录i的深度

就变成了一个静态区间查询问题

显然 还可用其它数据结构

这里洛谷的过了点这

然而没有吸氧 只是有个点差两毫秒TLE啊

#include <queue>
#include <vector>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
 
inline void Read(int &x)
{
    x = 0;
    char a = getchar();
    bool f = 0;
    while(a < '0'||a > '9') {if(a == '-') f = 1;a = getchar();}
    while(a >= '0'&&a <= '9') {x = x * 10 + a - '0';a = getchar();}
    if(f) x *= -1;
}
const int MAXN = 500001;
int tot,seq[MAXN << 1],Index[MAXN],dep[MAXN + 2];
//seq[]记录dfs序,Index记录结点在dfs序的位子,dep深度(说过了
vector<int> G[MAXN];
bool vis[MAXN];
inline void dfs(int x)
{
    seq[++tot] = x;
    Index[x] = tot;
    for(int i = 0;i < G[x].size();i++)
    {
        if(!vis[G[x][i]])
        {
            vis[G[x][i]] = 1;
            dep[G[x][i]] = dep[x] + 1;
            dfs(G[x][i]);
            seq[++tot] = x;
        }
    }
}
template<typename T>
inline T Min(T a,T b) {if(a < b) return a;return b;}
template<typename T>
inline void exchange(T &a,T &b) {T c = a;a = b;b = c;}
int tree[MAXN << 3],Left,Right;
//tree[MAXN << 3]一定是3,因为是针对seq[MAXN << 1]的
inline void tree_build(int l,int r,int k)
{
    if(l == r)
    {
        tree[k] = seq[l];
        return;
    }
    int mid = l + r >> 1;
    tree_build(l,mid,k << 1);
    tree_build(mid + 1,r,k << 1 ^ 1);
    if(dep[tree[k << 1]] < dep[tree[k << 1 ^ 1]])
        tree[k] = tree[k << 1];
    else tree[k] = tree[k << 1 ^ 1];
    //是比较深度,不是比较编号大小
}
inline int query(int l,int r,int k)
{
    if(Left <= l&&r <= Right)
        return tree[k];
    int mid = l + r >> 1,A1,A2;
    A1 = A2 = MAXN + 1;
    //main中写了dep[MAXN + 1] = 极大值
    if(Left <= mid) 
        A1 = query(l,mid,k << 1);
    if(mid < Right)
        A2 = query(mid + 1,r,k << 1 ^ 1);
    if(dep[A1] < dep[A2])
        return A1;
    return A2;
}
int main()
{
    dep[MAXN + 1] = 5000011;
    int n,m,i,S;
    Read(n),Read(m);
    Read(S);
    //它给定根
    for(i = 1;i <= n - 1;i++)
    {
        int u,v,w;
        Read(u),Read(v);
        G[u].push_back(v);
        G[v].push_back(u);
    }
    vis[S] = 1;
    dfs(S);
    tree_build(1,tot,1);
    while(m--)
    {
        int u,v;
        Read(u),Read(v);
        Left = Index[u],Right = Index[v];
        if(Left > Right) exchange(Left,Right);
        //可能顺序不对
        int LCA = query(1,tot,1);
        printf("%d\n",LCA);
    }
    return 0;
}

延伸

如果求 u 到 v 的最短距离

可以变成

power[u] + power[v] - 2 * power[LCA(u,v)]

倍增

#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
 
inline void Read(int &x)
{
    x = 0;
    char a = getchar();
    bool f = 0;
    while(a < '0'||a > '9') {if(a == '-') f = 1;a = getchar();}
    while(a >= '0'&&a <= '9') {x = x * 10 + a - '0';a = getchar();}
    if(f) x *= -1;
}
const int MAXN = 50001,MAXK = 50;
int dep[MAXN],f[MAXN][MAXK];
long long power[MAXN],all;
bool vis[MAXN];
vector<int> G[MAXN],Road[MAXN];
inline void dfs(int u)
{
	for(int i = 0;i < G[u].size();i++)
	{
		if(!vis[G[u][i]])
		{
			vis[G[u][i]] = 1;
			dep[G[u][i]] = dep[u] + 1;
			f[G[u][i]][0] = u;
			power[G[u][i]] = power[u] + Road[u][i];
			dfs(G[u][i]);
		}
	}
}
inline void adjust(int &u,int val)
{
	for(int i = MAXK - 1;i >= 0;i--)
		if(dep[f[u][i]] >= val)
			u = f[u][i];
}
inline int lca(int u,int v)
{
	if(dep[u] > dep[v]) adjust(u,dep[v]);
	else if(dep[u] < dep[v]) adjust(v,dep[u]);
	if(u == v) return u;
	for(int i = MAXK - 1;i >= 0;i--)
		if(f[u][i] != f[v][i])
			u = f[u][i],v = f[v][i];
	return f[u][0];
}
inline void count(int u,int v)
{
	int w = lca(u,v);
	all += power[u] + power[v] - power[w] * 2;
}
int main()
{
	int n;
	bool F = 0;
    while(~scanf("%d",&n))
    {
    	if(F) putchar('\n');
		F = 1;
		int i,m;
    	memset(vis,0,sizeof(vis));
    	memset(dep,0,sizeof(dep));
    	memset(f,0,sizeof(f));
    	memset(power,0,sizeof(power));
    	for(i = 1;i <= n;i++) G[i].clear(),Road[i].clear();
    	for(i = 1;i < n;i++)
    	{
    		int u,v,w;
    		Read(u),Read(v),Read(w);
    		u++,v++;
    		G[u].push_back(v);
    		G[v].push_back(u);
    		Road[u].push_back(w);
    		Road[v].push_back(w);
		}
		vis[1] = 1;
		dfs(1);
		f[1][0] = 1;
		for(i = 1;i < MAXK;i++)
			for(int j = 1;j <= n;j++)
				f[j][i] = f[f[j][i - 1]][i - 1];
		Read(m);
		while(m--)
		{
			int a,b,c;
			Read(a),Read(b),Read(c);
			a++,b++,c++;
			all = 0;
			count(a,b),count(b,c),count(a,c);
			printf("%lld\n",all / 2);
	 	}
	 	
	}
    return 0;
}
posted @ 2019-08-08 19:30  resftlmuttmotw  阅读(378)  评论(0编辑  收藏  举报