hdu 6031 Innumerable Ancestors lca + 二分

来源:互联网 发布:js系列减速机加油标准 编辑:程序博客网 时间:2024/05/23 01:19

题目:

http://acm.hdu.edu.cn/showproblem.php?pid=6031

题意:

给定一个无向树形图,1为根,对于每次查询,给出两个集合,问两个集合中各选出一个点的最近公共祖先的深度最深为多少

思路:

用倍增求lca,然后二分枚举答案,求出第一个集合中的点在枚举的深度上的祖先,并标记这些祖先,然后求第二个集合中的点在枚举的深度上的祖先,检查两个集合在枚举深度上的祖先有没有重合,有重合就意味着在枚举深度上有lca

#include <bits/stdc++.h>using namespace std;const int N = 100000 + 10, INF = 0x3f3f3f3f;struct edge{    int to, next;}g[N*2];int cnt, head[N];int dep[N], dis[N], fat[N][20];int x[N], y[N];bool vis[N];void init(){    cnt = 0;    memset(head, -1, sizeof head);}void add_edge(int v, int u){    g[cnt].to = u, g[cnt].next = head[v], head[v] = cnt++;}void dfs(int v, int fa, int d){    dep[v] = d, fat[v][0] = fa;    for(int i = head[v]; ~i; i = g[i].next)    {        int u = g[i].to;        if(u == fa) continue;        dfs(u, v, d+1);    }}void lca_init(int n){    for(int j = 1; (1<<j) <= n; j++)        for(int i = 1; i <= n; i++)            fat[i][j] = fat[fat[i][j-1]][j-1];}int lca(int v, int u){    if(dep[v] < dep[u]) swap(v, u);    int d = dep[v] - dep[u];    for(int i = 0; (d>>i) != 0; i++)        if((d>>i) & 1) v = fat[v][i];    if(v == u) return v;    for(int i = 18; i >= 0; i--)        if(fat[v][i] != fat[u][i]) v = fat[v][i], u = fat[u][i];    return fat[v][0];}int query(int v, int d){    if(d < 0) return -1;    if(d == 0) return v;    for(int i = 0; (d>>i) != 0; i++)        if((d>>i) & 1) v = fat[v][i];    return v;}bool check(int mid, int kx, int ky){    set<int> ste;    for(int i = 1; i <= kx; i++)    {        int d = dep[x[i]] - mid;        int v = query(x[i], d);        if(v != -1) ste.insert(v);    }    for(int i = 1; i <= ky; i++)    {        int d = dep[y[i]] - mid;        int v = query(y[i], d);        if(ste.count(v)) return true;    }    return false;}int main(){    int n, m;    while(~ scanf("%d%d", &n, &m))    {        init();        int a, b;        for(int i = 1; i <= n-1; i++)        {            scanf("%d%d", &a, &b);            add_edge(a, b); add_edge(b, a);        }        dfs(1, 0, 1);        lca_init(n);        int kx, ky;        for(int i = 1; i <= m; i++)        {            int mx = 0;            scanf("%d", &kx);            for(int j = 1; j <= kx; j++) scanf("%d", &x[j]), mx = max(mx, dep[x[j]]);            scanf("%d", &ky);            for(int j = 1; j <= ky; j++) scanf("%d", &y[j]);            int l = 1, r = mx, ans;            while(l <= r)            {                int mid = (l + r) >> 1;                if(check(mid, kx, ky)) ans = mid, l = mid + 1;                else r = mid - 1;            }            printf("%d\n", ans);        }    }    return 0;}

我自己写了一个挺暴力的方法,首先把两个集合中的点分别按照深度从大到小排序,然后直接两重循环暴力,一个优化是如果当前点的深度小于已经求出的lca深度,就continue,然后这样就过了。。。

#include <bits/stdc++.h>using namespace std;typedef long long ll;const int N = 100010, INF = 0x3f3f3f3f;struct edge{    int to, next;}g[N*2];int cnt, head[N];int dis[N];int dp[20][N*2];int tot, dep[N*2], ord[N*2], fir[N];int x[N], y[N];void init(){    cnt = 0;    memset(head, -1, sizeof head);    tot = 0;}void add_edge(int v, int u){    g[cnt].to = u, g[cnt].next = head[v], head[v] = cnt++;}void dfs(int v, int fa, int d){    ord[++tot] = v, dep[tot] = d, fir[v] = tot;    for(int i = head[v]; i != -1; i = g[i].next)    {        int u = g[i].to;        if(u == fa) continue;        dfs(u, v, d + 1);        ord[++tot] = v, dep[tot] = d;    }}void ST(int n){    for(int i = 1; i <= n; i++)        dp[0][i] = i;    for(int i = 1; (1<<i) <= n; i++)        for(int j = 1; j <= n - (1<<i) + 1; j++)            dp[i][j] = dep[dp[i-1][j]] < dep[dp[i-1][j+(1<<(i-1))]] ? dp[i-1][j] : dp[i-1][j+(1<<(i-1))];}int RMQ(int l, int r){    int k = log(r - l + 1) / log(2.0);    return dep[dp[k][l]] < dep[dp[k][r-(1<<k)+1]] ? dp[k][l] : dp[k][r-(1<<k)+1];}int LCA(int v, int u){    v = fir[v], u = fir[u];    if(v > u) swap(v, u);    int res = RMQ(v, u);    return ord[res];}int main(){    int n, m;    while(~ scanf("%d%d", &n, &m))    {        init();        int a, b;        for(int i = 1; i <= n-1; i++)        {            scanf("%d%d", &a, &b);            add_edge(a, b); add_edge(b, a);        }        dfs(1, 0, 1);        ST(2*n - 1);        int kx, ky;        for(int i = 1; i <= m; i++)        {            scanf("%d", &kx);            for(int j = 1; j <= kx; j++) scanf("%d", &x[j]);            scanf("%d", &ky);            for(int j = 1; j <= ky; j++) scanf("%d", &y[j]);            sort(x + 1, x + 1 + kx, [](int a, int b){return dep[fir[a]] > dep[fir[b]];});            sort(y + 1, y + 1 + ky, [](int a, int b){return dep[fir[a]] > dep[fir[b]];});            int ans = 0;            for(int j = 1; j <= kx; j++)            {                if(ans >= dep[fir[x[j]]]) continue;                for(int k = 1; k <= ky; k++)                {                    int lca = LCA(x[j], y[k]);                    ans = max(ans, dep[fir[lca]]);                }            }            printf("%d\n", ans);        }    }    return 0;}
原创粉丝点击