POJ 1330 Nearest Common Ancestors (LCA模板题)

55 篇文章 0 订阅
16 篇文章 0 订阅

题目

最裸的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;
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值