1. 程式人生 > >洛谷 P3379 【模板】最近公共祖先(LCA) (線上倍增+離線Tarjan)

洛谷 P3379 【模板】最近公共祖先(LCA) (線上倍增+離線Tarjan)

題目描述

如題,給定一棵有根多叉樹,請求出指定兩個點直接最近的公共祖先。

輸入格式:

第一行包含三個正整數N、M、S,分別表示樹的結點個數、詢問的個數和樹根結點的序號。

接下來N-1行每行包含兩個正整數x、y,表示x結點和y結點之間有一條直接連線的邊(資料保證可以構成樹)。

接下來M行每行包含兩個正整數a、b,表示詢問a結點和b結點的最近公共祖先。

輸出格式:

輸出包含M行,每行包含一個正整數,依次為每一個詢問的結果。

輸入樣例#1:

5 5 4
3 1
2 4
5 1
1 4
2 4
3 2
3 5
1 2
4 5

輸出樣例#1:

4
4
1
4
4

思路

題如其名,就是lca的模板。我就存一存我的lca模板~

離線Tarjan

#include <cstdio>
#include <cstring>
#include <cctype>
#include <stdlib.h>
#include <string>
#include <map>
#include <iostream>
#include <stack>
#include <cmath>
#include <queue>
#include <vector> #include <algorithm> using namespace std; typedef long long ll; #define inf 1000000 #define mem(a,b) memset(a,b,sizeof(a)) const int N=500000+7; int pre[N],first[N],first2[N],tot,tot2; bool vis[N];//標記有沒有詢問 int n; struct edge { int v,next; } e[2*N]; struct Query { int v,w,next; } query[2
*N]; void add_edge(int u,int v) { e[tot].v=v; e[tot].next=first[u]; first[u]=tot++; } void add_query(int u,int v) { query[tot2].v=v; query[tot2].w=-1; query[tot2].next=first2[u]; first2[u]=tot2++; } int find(int x) { return x==pre[x]?x:pre[x]=find(pre[x]); } int lca(int u,int fa) { for(int i=first[u]; ~i; i=e[i].next) { int v=e[i].v; if(v==fa) continue; lca(v,u); pre[v]=u; } vis[u]=1; for(int i=first2[u]; ~i; i=query[i].next) { int v=query[i].v; if(vis[v]) query[i].w=find(v); } } void init() { mem(first,-1); mem(first2,-1); mem(vis,0); tot=0; tot2=0; for(int i=1; i<=n; i++) pre[i]=i; } int main() { int m,s,u,v; scanf("%d%d%d",&n,&m,&s); init(); for(int i=1; i<n; i++) { scanf("%d%d",&u,&v); add_edge(u,v); add_edge(v,u); } for(int i=1; i<=m; i++) { scanf("%d%d",&u,&v); add_query(u,v); add_query(v,u); } lca(s,pre[s]); for(int i=0; i<tot2; i++) if(query[i].w!=-1) printf("%d\n",query[i].w); return 0; }

線上倍增

#include <cstdio>
#include <cstring>
#include <cctype>
#include <stdlib.h>
#include <string>
#include <map>
#include <iostream>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <algorithm>
using namespace std;
typedef long long ll;
#define inf 1000000
#define mem(a,b) memset(a,b,sizeof(a))
const int N=500000+7;

int n,m,s;
int tot;
int first[N],d[N],p[N][21];

struct edge
{
    int v,next;
} e[2*N];

void add_edge(int u,int v)
{
    e[tot].v=v;
    e[tot].next=first[u];
    first[u]=tot++;
}

void dfs(int u,int fa)
{
    d[u]=d[fa]+1;
    p[u][0]=fa;
    for(int i=1; (1<<i)<=d[u]; i++)
        p[u][i]=p[p[u][i-1]][i-1];
    for(int i=first[u]; ~i; i=e[i].next)
    {
        int v=e[i].v;
        if(v!=fa)
            dfs(v,u);
    }
}

int lca(int a,int b)
{
    if(d[a]>d[b]) swap(a,b);//保證a在b點的上方
    for(int i=20; i>=0; i--)
        if(d[a]<=d[b]-(1<<i))
            b=p[b][i];  //把b移到和a同一個深度
    if(a==b) return a;
    for(int i=20; i>=0; i--)
    {
        if(p[a][i]==p[b][i])
            continue;
        else
            a=p[a][i],b=p[b][i];//一起向上跳躍
    }
    return p[a][0];
}


void init()
{
    tot=0;
    mem(first,-1);
    mem(d,0);
    mem(p,0);
}
int main()
{
    init();
    int a,b;
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1; i<n; i++)
    {
        scanf("%d%d",&a,&b);
        add_edge(a,b);
        add_edge(b,a);
    }
    dfs(s,0);
    for(int i=1; i<=m; i++)
    {
        scanf("%d%d",&a,&b);
        printf("%d\n",lca(a,b));
    }
    return 0;
}