题目
最裸的LCA模板题
分析
可以做的方式很多
- 离线 Tarjian算法 O( n + q )
- 在线 LCA倍增法 O ( n + q(logn) )
- 在线 dfs + ST表算法 预处理O (nlogn) 查询 O(1)
下面给出 两种储存方式邻接表和链式前向星,后者更快
①:Tarjian + 邻接表, 62ms
(POJ不支持万能头文件,和c++11,所以我注释了一些代码)
#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
#include <cmath>
#include <map>
#include <set>
#include <string>
#include <iostream>
#include <algorithm>
#include <functional>
#include <stack>
#include <ctime>
#include <cstdlib>
// #include <bits/stdc++.h>
// #pragma GCC diagnostic error "-std=c++11"
#define d(x) cout << (x) << endl
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 1e4 + 10;
int t, n;
int x, y;
int ans;
vector<int> v[N]; //邻接表
vector<int> ask[N];
int fa[N]; //并查集
int root[N]; //找根节点
int vis[N]; //标记数组
void init()
{
memset(vis, 0, sizeof(vis));
memset(root, 0, sizeof(root));
for (int i = 1; i <= n; i++){
fa[i] = i;
v[i].clear();
ask[i].clear();
}
}
int find(int x)
{
return fa[x] = fa[x] == x ? x : find(fa[x]);
}
void tarjian(int root) //dfs 序
{
// for(auto x : v[root]){ //遍历root的每个子树
// tarjian(x);
// fa[x] = root; //回溯
// }
for(int i = 0; i < v[root].size(); i++){
int x = v[root][i];
tarjian(x);
fa[x] = root;
}
vis[root] = 1; //访问过root节点了
// for(auto x : ask[root]){ //查询跟root有关系
// if(vis[x]){ //并且访问过的节点
// ans = find(x);
// return;
// }
// }
for(int i = 0; i < ask[root].size(); i++){
int x = ask[root][i];
if(vis[x]){
ans = find(x);
return;
}
}
}
int main()
{
for(scanf("%d", &t); t; t--){
scanf("%d", &n);
init(); //初始化
for(int i = 1; i < n; i++){
scanf("%d%d", &x, &y);
v[x].push_back(y);
root[y]++;
}
scanf("%d%d", &x, &y);
ask[x].push_back(y);
ask[y].push_back(x);
for(int i = 1; i <= n; i++){
if(!root[i]){
tarjian(i);
break;
}
}
printf("%d\n", ans);
}
return 0;
}
②:Tarjian + 链式前向星 32ms
#include <cstdio>
#include <vector>
#include <queue>
#include <cstring>
#include <cmath>
#include <map>
#include <set>
#include <string>
#include <iostream>
#include <algorithm>
#include <functional>
#include <stack>
#include <ctime>
#include <cstdlib>
// #include <bits/stdc++.h>
// #pragma GCC diagnostic error "-std=c++11"
#define d(x) cout << (x) << endl
#define INF 0x3f3f3f3f
#define eps 1e-8
using namespace std;
typedef long long ll;
const int mod = 1e9 + 7;
const int N = 1e4 + 10;
int t, n;
int x, y;
int ans;
int cnt;
struct node{ //链式前向星存树,按边存
int next; //以当前边起始点为起始点的下一条边
int to; //当前边终止点
} e[N]; //边集数组
int head[N]; //head[i] 代表i节点为起始点的边集数组存放下标
vector<int> ask[N];
int fa[N];
int root[N];
int vis[N];
void init()
{
cnt = 0;
memset(vis, 0, sizeof(vis));
memset(root, 0, sizeof(root));
memset(head, -1, sizeof(head));
for(int i = 1; i <= n; i++){
fa[i] = i;
ask[i].clear();
}
}
void add(int u, int v)
{
e[cnt].to = v;
e[cnt].next = head[u];
head[u] = cnt++;
}
int find(int x)
{
return fa[x] = fa[x] == x ? x : find(fa[x]);
}
void tarjian(int root)
{
for(int i = head[root]; i != -1; i = e[i].next){
int x = e[i].to;
tarjian(x);
fa[x] = root;
}
vis[root] = 1;
for(int i = 0; i < ask[root].size(); i++){
int x = ask[root][i];
if(vis[x]){
ans = find(x);
return;
}
}
}
int main()
{
for(scanf("%d", &t); t; t--){
scanf("%d", &n);
init();
for(int i = 1; i < n; i++){
scanf("%d%d", &x, &y);
add(x, y);
root[y]++;
}
scanf("%d%d", &x, &y);
ask[x].push_back(y);
ask[y].push_back(x);
for (int i = 1; i <= n; i++){
if(!root[i]){
tarjian(i);
break;
}
}
printf("%d\n", ans);
}
return 0;
}