POJ 1330 Nearest Common Ancestors LCA

题目链接:

http://poj.org/problem?id=1330

题意:

给你一颗有根树,最后输入一对数(a,b),叫你求a和b的公共祖先。

裸的lca,数据也很小,拿来练手不错。

题解:

1、tarjan_lca,离线,线性时间复杂度

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<vector>
 5 using namespace std;
 6 
 7 const int maxn = 1e4 + 10;
 8 
 9 int n,root;
10 int deg[maxn],vis[maxn],p[maxn];
11 vector<int> tree[maxn],que[maxn];
12 
13 void init() {
14     memset(deg, 0, sizeof(deg));
15     for (int i = 0; i <= n; i++) tree[i].clear();
16     for (int i = 0; i <= n; i++) que[i].clear();
17     memset(vis, 0, sizeof(vis));
18     root = -1;
19 }
20 //并查集:
21 int fin(int x) { return p[x] = p[x] == x ? x : fin(p[x]); }
22 void uni(int a, int b) {
23     int pa = fin(a);
24     int pb = fin(b);
25     //不能写成p[pa]=pb,和tarjan算法有关;
26     p[pb] = pa;
27 }
28 
29 bool tarjan_LCA(int rt,int& ans) {
30     p[rt] = rt;
31     for (int i = 0; i < tree[rt].size(); i++) {
32         int v = tree[rt][i];
33         if (tarjan_LCA(v, ans)) return true;
34         //把v并到rt上
35         uni(rt, v);
36     }
37     //涂黑这一点
38     vis[rt] = 1;
39     //处理与rt有关的查询
40     for (int i = 0; i < que[rt].size(); i++) {
41         int qv = que[rt][i];
42         if (vis[qv]) {
43             ans = fin(qv);
44             return true;
45         }
46     }
47     return false;
48 }
49 
50 int main() {
51     int tc;
52     scanf("%d", &tc);
53     while (tc--) {
54         scanf("%d", &n);
55         init();
56         for (int i = 0; i < n - 1; i++) {
57             int u, v;
58             scanf("%d%d", &u, &v);
59             tree[u].push_back(v);
60             deg[v]++;
61         }
62         int qu, qv;
63         scanf("%d%d", &qu, &qv);
64         que[qu].push_back(qv);
65         que[qv].push_back(qu);
66         for (int i = 1; i <= n; i++) if (deg[i] == 0) {
67             root = i;
68         }
69         int ans;
70         tarjan_LCA(root,ans);
71         printf("%d\n", ans);
72     }
73     return 0;
74 }
View Code

2、在线LCA

 1 /*
 2 author: fenice
 3 algorithm: 在线LCA
 4 time: 预处理O(nlogn),查询O(logn)
 5 */
 6 
 7 #include<iostream>
 8 #include<cstdio>
 9 #include<cstring>
10 #include<vector>
11 #include<algorithm>
12 using namespace std;
13 
14 const int maxn = 1e4 + 10;
15 
16 int n, root;
17 int par[maxn];
18 int anc[maxn][33];
19 vector<int> tree[maxn];
20 
21 int hig[maxn];//求深度(根节点为0)
22 void dfs(int cur, int h) {
23     hig[cur] = h;
24     for (int i = 0; i < tree[cur].size(); i++) {
25         dfs(tree[cur][i], h + 1);
26     }
27 }
28 
29 //预处理:anc[i][j]表示i节点的第2^j个祖先,它的父亲是它的第2^0个祖先,以此从下往上推。
30 void pre() {
31     for (int i = 0; i < n; i++) anc[i][0] = par[i];
32     for (int j = 1; j < 32; j++) {
33         for (int i = 0; i < n; i++) {
34             int x = anc[i][j - 1];
35             if (x != -1) {
36                 anc[i][j] = anc[x][j - 1];
37             }
38         }
39     }
40 }
41 
42 //在线LCA:
43 int solve(int p, int q) {
44     if (hig[p] < hig[q]) swap(p, q);
45     int log;
46     for (log = 0; (1 << log) <= hig[p]; log++); log--;
47 
48     //把p提到与q同一层,这里是二进制表示的一个应用。
49     //例子:如果x比y大,那么x减去若干个(1<<i)一定可以变成y
50     for (int i = log; i >= 0; i--) {
51         if (anc[p][i] != -1 && hig[anc[p][i]] >= hig[q]) p = anc[p][i];
52     }
53 
54     //这里比较容易漏
55     if (p == q) return p;
56 
57     //p,q一起往上提
58     for (int i = log; i >= 0; i--) {
59         if (anc[p][i] != anc[q][i]) {
60             p = anc[p][i];
61             q = anc[q][i];
62         }
63     }
64     return par[p];
65 }
66 
67 void init() {
68     memset(par, -1, sizeof(par));
69     for (int i = 0; i <= n; i++) tree[i].clear();
70     memset(anc, -1, sizeof(anc));
71     root = -1;
72 }
73 
74 int main() {
75     int tc;
76     scanf("%d", &tc);
77     while (tc--) {
78         scanf("%d", &n);
79         init();
80         for (int i = 0; i < n - 1; i++) {
81             int u, v;
82             scanf("%d%d", &u, &v); u--; v--;
83             tree[u].push_back(v);
84             par[v] = u;
85         }
86         //找根
87         for (int i = 0; i < n; i++) if (par[i] == -1) {
88             root = i; break;
89         }
90 
91         dfs(root, 0);
92         pre();
93 
94         int qu, qv;
95         scanf("%d%d", &qu, &qv); qu--, qv--;
96         printf("%d\n", solve(qu, qv) + 1);
97     }
98     return 0;
99 }
View Code

3、树链剖分

#include<map>
#include<set>
#include<cmath>
#include<queue>
#include<stack>
#include<ctime>
#include<vector>
#include<cstdio>
#include<string>
#include<bitset>
#include<cstdlib>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<functional>
using namespace std;
#define X first
#define Y second
#define mkp make_pair
#define lson (o<<1)
#define rson ((o<<1)|1)
#define mid (l+(r-l)/2)
#define sz() size()
#define pb(v) push_back(v)
#define all(o) (o).begin(),(o).end()
#define clr(a,v) memset(a,v,sizeof(a))
#define bug(a) cout<<#a<<" = "<<a<<endl
#define rep(i,a,b) for(int i=a;i<(b);i++)
#define scf scanf
#define prf printf

typedef long long LL;
typedef vector<int> VI;
typedef pair<int,int> PII;
typedef vector<pair<int,int> > VPII;

const int INF=0x3f3f3f3f;
const LL INFL=0x3f3f3f3f3f3f3f3fLL;
const double eps=1e-8;
const double PI = acos(-1.0);

const int maxn = 22222;

vector<int> G[maxn];

///val:边权转点权,fat:父亲,dep:深度,siz:子树节点个数
int fat[maxn], dep[maxn], siz[maxn];
///son:重儿子,top:链顶,id:dfs序
int son[maxn], top[maxn], id[maxn];
int n;

///求dep,siz;
void dfs(int u, int d) {
    dep[u] = d;
    siz[u] = 1;
    int ma = -INF;
    son[u]=0;
    for (int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        dfs(v, d + 1);
        siz[u] += siz[v];
        if (ma < siz[v]) {
            son[u] = v;
            ma = siz[v];
        }
    }
}
///求top,id
int _tot;
void dfs2(int u,int t) {
    top[u] = t;
    id[u] = ++_tot;
    if (son[u]) dfs2(son[u], t);
    for (int i = 0; i < G[u].size(); i++) {
        int v = G[u][i];
        if (v == son[u]) continue;
        ///轻链的下端点top等于它自己
        dfs2(v,v);
    }
}

int solve(int u, int v) {
    int tu = top[u], tv = top[v];
    while (tu != tv) {
        if (dep[tu] < dep[tv]) {
            swap(tu, tv);
            swap(u, v);
        }
        u = fat[tu];
        tu = top[u];
    }
    return dep[u]>dep[v]?v:u;
}

void init() {
    for (int i = 0; i <= n; i++) G[i].clear();
    for(int i=0; i<=n; i++) fat[i]=-1;
    _tot = 0;
}

int main() {
    int tc;
    scf("%d",&tc);
    while(tc--) {
        scf("%d",&n);
        init();
        rep(i,0,n-1) {
            int u,v;
            scf("%d%d",&u,&v);
            G[u].pb(v);
            fat[v]=u;
        }
        int rt=0;
        for(int i=1;i<=n;i++){
            if(fat[i]==-1){
                rt=i; break;
            }
        }

        dfs(rt,1);
        dfs2(rt,rt);

        int qu,qv;
        scf("%d%d",&qu,&qv);
        prf("%d\n",solve(qu,qv));

    }
    return 0;
}
View Code

 

posted @ 2016-04-13 00:33  fenicnn  阅读(220)  评论(0编辑  收藏  举报