图论--LCA

离线Tarjan

算法思想

离线处理,先记录需要找的点

链式前向星模板

#include <bits/stdc++.h>
using namespace std;
#define mem(a,b) memset(a,b,sizeof(a))
#define cin(a) scanf("%d",&a)
#define pii pair<int,int>
#define ll long long
#define gcd __gcd
const int inf = 0x3f3f3f3f;
const int maxn = 1000100;
const int M = 1e9+7;
int n,m,s,cnt;

int head[maxn],to[maxn],Next[maxn];        //链式前向星
vector<pii> q[maxn];           //询问
int pre[maxn];                 //先驱
bool vis[maxn];                //是否访问过
int ans[maxn];                 //记录第i个询问的答案

void add(int u,int v)
{
    to[cnt] = v;Next[cnt] = head[u]; head[u] = cnt;
    cnt++;
}

int find(int x)
{
    return x == pre[x] ? x : pre[x] = find(pre[x]);
}

void dfs(int u,int from)
{
    for(int i = head[u]; i != -1; i = Next[i])
    {   
        int v = to[i];
        if(v != from)
        {
            dfs(v,u);
            pre[v] = u;
        }
    }
    for(auto j : q[u])
    {
        if(vis[j.first])
        {
            ans[j.second] = find(j.first);
        }
    }
    vis[u] = 1;
}

int main()
{
#ifdef ONLINE_JUDGE
#else
    freopen("data.in", "r", stdin);
    //freopen("data.out", "w", stdout);
#endif
    scanf("%d%d%d",&n,&m,&s);
    for(int i = 1; i <= n; i++) head[i] = -1,pre[i] = i;
    for(int i = 1,u,v; i < n; i++)
    {
        scanf("%d%d",&u,&v);
        add(u,v);add(v,u);
    }
    for(int i = 1,u,v; i <= m; i++)
    {
        scanf("%d%d",&u,&v);
        q[u].push_back(make_pair(v,i));
        q[v].push_back(make_pair(u,i));
    }
    dfs(s,0);
    for(int i = 1; i <= n; i++) printf("%d\n",ans[i]);
    return 0;
}

倍增LCA

预处理过程:O(nlogn)
先用dfs或者bfs求出每个点的deep,然后更新每个点的fa数组
查询过程:O(logn)
1.假设x的深度更小(在y的上面)
2.y开始向上跳,跳到和x同深度
3.x,y一起向上跳

int d[maxn];      //深度
int fa[maxn][20];     //节点i的2^i级祖先

void dfs(int u,int pre)
{
    d[u] = d[pre]+1;
    fa[u][0] = pre;
    for(int i = 1; (1<<i) <= d[u]; i++)
    {
        fa[u][i] = fa[fa[u][i-1]][i-1];
    }
    for(int i = head[u]; i ; i = Next[i])
    {   
        int v = to[i];
        if(v != pre)
        {
            dfs(v,u);
        }
    }
}

//t = log(n)/log(2)+1; 可能的最大深度

int lca(int x,int y)
{
    if(d[x] > d[y]) swap(x,y);      //让x的深度更小
    for(int i = t; i >= 0; i--)     //首先y要跳到和x深度相同
    {
        if(d[fa[y][i]] >= d[x]) y = fa[y][i];       //如果y^i还是在x下面,跳到y^i
    }
    //此时,x和y的深度一定相等
    if(x == y) return x;            //如果就是x,return
    for(int i = t; i >= 0; i--)     //从打的开始
    {
        if(fa[x][i] != fa[y][i])    //如果x的第i层和y的第i层不相同,如果相同说明跳过头了
        {
            x = fa[x][i];y=fa[y][i];
        }
    }
    return fa[x][0];                //此时的x的父节点一定是lca
}

参考博客

https://www.cnblogs.com/JVxie/p/4854719.html
https://www.cnblogs.com/hulean/p/11144059.html
https://blog.csdn.net/zhaoxinxin1234/article/details/89764483

posted @ 2019-11-10 20:36  hezongdnf  阅读(151)  评论(0编辑  收藏  举报