洛谷 P3379 【模板】最近公共祖先(LCA) (線上倍增+離線Tarjan)
阿新 • • 發佈:2019-02-20
題目描述
如題,給定一棵有根多叉樹,請求出指定兩個點直接最近的公共祖先。
輸入格式:
第一行包含三個正整數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;
}