【算法】【LCA】【tarjan】【倍增】【RMQ】

一曲冷凌霜 提交于 2019-11-26 16:46:36

呃,这个常用但是我一直不会

Tarjan

  Tarjan 算法基于 dfs ,在 dfs 的过程中,对于每个节点位置的询问做出相应的回答。

  dfs 的过程中,当一棵子树被搜索完成之后,就把他和他的父亲合并成同一集合;在搜索当前子树节点的询问时,如果该询问的另一个节点已经被访问过,那么该编号的询问是被标记了的,于是直接输出当前状态下,另一个节点所在的并查集的祖先;如果另一个节点还没有被访问过,那么就做下标记,继续 dfs 。

//tarjian,边建边 边回答问题
#include<cstdio>
#include<cstdlib>
#include<vector>
using namespace std;
int n,m,rt;
const int N=500003,M=500003;
int ans[M];
vector <int> e[N];
struct node
{
    int v,id;
    node(int vv,int ii)
    { v=vv,id=ii; }
    node(){}
};
vector <node> q[N];

int fa[N];
int find(int x)
{ return fa[x]==0?x:fa[x]=find(fa[x]); }
bool vis[N];
void dfs(int x,int f)
{
    int sz=e[x].size();
    for(int i=0;i<sz;i++)
    {
        int v=e[x][i];
        if(v!=f) dfs(v,x),fa[v]=x;
    }
    vis[x]=true;
    
    sz=q[x].size();
    for(int i=0;i<sz;i++)
    {
        int v=q[x][i].v ;
        if(!ans[q[x][i].id ] && vis[v])
            ans[q[x][i].id ]=find(v);
    }
}

int main()
{
    scanf("%d%d%d",&n,&m,&rt);
    int u,v;
    for(int i=1;i<n;i++) 
        scanf("%d%d",&u,&v),e[u].push_back(v),e[v].push_back(u);
    for(int i=0;i<m;i++)
        scanf("%d%d",&u,&v),q[u].push_back(node(v,i)),q[v].push_back(node(u,i));
    
    dfs(rt,0);
    
    for(int i=0;i<m;i++) printf("%d\n",ans[i]);
    
    return 0;
} 

倍增

就从父亲更新到儿子

//tarjian,边建边 边回答问题
#include<cstdio>
#include<cstdlib>
#include<vector>
#include<algorithm>
using namespace std;
int n,m,rt;
const int N=500003,M=500003;
int ans[M];
vector <int> e[N];
int lg[N];

inline int read()
{
    int x=0;char c=getchar();
    while(c<'0' || c>'9') c=getchar();
    while(c>='0' && c<='9') x=(x<<1)+(x<<3) +c-'0',c=getchar();
    return x;
}

int dep[N];
int fa[N][19];//2的0-18次方,,,全跳最高能到2^19 
void dfs(int x,int f)
{
    fa[x][0]=f;
    dep[x]=dep[f]+1;
    for(int i=1;i<19;i++)
        fa[x][i]=fa[fa[x][i-1]][i-1];
    
    int sz=e[x].size();
    for(int i=0;i<sz;i++)
    {
        int v=e[x][i];
        if(v!=f) dfs(v,x);
    }
} 
void get_log()
{
    for(int i=1;i<=n;i++) //预先算出log_2(i)+1的值,用的时候直接调用就可以了
        lg[i]=lg[i-1]+(1<<lg[i-1]==i);  //看不懂的可以手推一下
}//还挺快

int LCA(int u,int v)
{
    if(dep[u]<dep[v]) swap(u,v); 
    int dis=dep[u]-dep[v];
    for(int i=0;dis;i++)
        if(dis & (1<<i)) 
        {
            dis^=(1<<i);
            u=fa[u][i];
        }
    if(u==v) return u;
    
    for(int i=lg[dep[u]]-1;i>=0;i--)
        if(fa[u][i]!=fa[v][i])
            u=fa[u][i],v=fa[v][i];
    
    return fa[u][0];
} 

int main()
{
    n=read(),m=read(),rt=read();
    int u,v;
    for(int i=1;i<n;i++) 
        u=read(),v=read(),e[u].push_back(v),e[v].push_back(u);
    
    dfs(rt,0);
    
    get_log();
    for(int i=0;i<m;i++)
    {
        u=read(),v=read();
        printf("%d\n",LCA(u,v));
    }
    return 0;
} 
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!