HDU - 6031 (lca) (暴力剪枝 或者 二分)

Innumerable Ancestors

HDU - 6031


题意:

寻找两个集合的LCA的最深深度;


第一种方法暴力:

就是将AB集合中结点按照深度递减排序以后循环查找,当已得到答案大于等于当前深度时结束循环;

代码:

#include <iostream>
#include<algorithm>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>

using namespace std;
const int maxn=1e5+5;

int  p[maxn][30];
int a[maxn];
int b[maxn];
int deep[maxn];
int m,n;
vector<int> eg[maxn];
void dfs(int fr,int u,int de)
{
    deep[u]=de;
    for(int i=1; (1<<i)<=n; i++)
    {
        if(p[u][i-1]!=-1)
            p[u][i]=p[p[u][i-1]][i-1];
    }
    for(int i=0; i<eg[u].size(); i++)
    {
        int v=eg[u][i];
        if(v==fr)
            continue;
        p[v][0]=u;
        dfs(u,v,de+1);
    }
}
void init()
{
    memset(p,-1,sizeof(p));
   // memset(deep,0,sizeof(deep));

    dfs(-1,1,1);
}
int lca(int a,int b)
{
    if(deep[a]<deep[b]) swap(a,b);
    int t;
    for(int i=0; (1<<i)<=deep[a]; i++)
    {
        t=i;
    }
    for(int i=t; i>=0; i--)
    {
        if((deep[a]-(1<<i))>=deep[b])
            a=p[a][i];
    }
    if(a==b)
        return a;
    for(int i=t; i>=0; i--)
    {
        if(p[a][i]!=-1&&p[a][i]!=p[b][i])
        {
            a=p[a][i];
            b=p[b][i];
        }
    }
    return p[a][0];
}
int cmp(int a,int b)
{
    return deep[a]>deep[b];
}
int main()
{

    while(~scanf("%d%d",&n,&m))
    {
        int ans=0;
        for(int i=0; i<=n; i++)
        {
            eg[i].clear();
        }
        for(int i=0; i<n-1; i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            eg[x].push_back(y);
            eg[y].push_back(x);
        }
        init();
        while(m--)
        {
            int ka,kb;
            ans=0;
            scanf("%d",&ka);
            for(int i=0; i<ka; i++)
                scanf("%d",&a[i]);
            scanf("%d",&kb);
            for(int i=0; i<kb; i++)
                scanf("%d",&b[i]);
            sort(a,a+ka,cmp);
            sort(b,b+kb,cmp);
            for(int i=0; i<ka; i++)
            {
                 //cout<<"a[i]="<<a[i]<<" de="<<deep[a[i]]<<endl;
                if(deep[a[i]]<=ans)
                    break;
                for(int j=0; j<kb; j++)
                {
                    if(deep[b[j]]<=ans)
                        break;
                    //cout<<"a="<<a[i]<<" b="<<b[j]<<" lca="<<lca(a[i],b[j])<<endl;

                    ans=max(ans,deep[lca(a[i],b[j])]);
                }
            }
             printf("%d\n",ans);
        }

    }
    return 0;
}

第二种 lca+二分

二分深度,找出当前深度中A的祖先,然后看B中在当前深度有无相同祖先;

#include <iostream>
#include<algorithm>
#include<cstdio>
#include<string>
#include<cstring>
#include<vector>
#include<map>

using namespace std;
const int maxn=1e5+5;

int p[maxn][30];
int a[maxn];
int b[maxn];
int deep[maxn];
int m,n;
 int ka,kb;
vector<int> eg[maxn];
void dfs(int fr,int u,int de)
{
    deep[u]=de;
    for(int i=1; (1<<i)<=n; i++)
    {
        if(p[u][i-1]!=-1)
            p[u][i]=p[p[u][i-1]][i-1];
    }
    for(int i=0; i<eg[u].size(); i++)
    {
        int v=eg[u][i];
        if(v==fr)
            continue;
        p[v][0]=u;
        dfs(u,v,de+1);
    }
}
void init()
{
    memset(p,-1,sizeof(p));
    dfs(-1,1,1);
}
int get(int u,int de)
{
    if(de<0) return -1;
    if(de==0) return u;
    else
    {
        for(int i=20;i>=0;i--)
        {
            if(de>=(1<<i))
            {
                u=p[u][i];
                de-=(1<<i);
            }
        }
    }
    return u;
}
map<int,int> mp;
int check(int depth)
{
    mp.clear();
    for(int i=0;i<ka;i++)
    {
        int de=deep[a[i]]-depth;
        int v=get(a[i],de);
        if(v!=-1)
        {
              mp[v]=1;
            //  if(depth==2)
             // cout<<"v="<<v<<endl;
        }

    }
    for(int i=0;i<kb;i++)
    {
        int de=deep[b[i]]-depth;
        int v=get(b[i],de);
       // if(depth==2)
       // cout<<" bv="<<v<<endl;
        if(v!=-1&&mp[v])
             return 1;
    }
    return 0;
}
int solve(int l,int r)
{
    while(l+1<r)
    {
        //cout<<l<<" "<<r<<endl;
        int mid=(l+r)>>1;
        if(check(mid))
           l=mid;
        else
            r=mid-1;
    }
    if(check(r))
        return r;
    return l;
}
int main()
{
    while(~scanf("%d%d",&n,&m))
    {
        int ans=0;
        for(int i=0; i<=n; i++)
        {
            eg[i].clear();
        }
        for(int i=0; i<n-1; i++)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            eg[x].push_back(y);
            eg[y].push_back(x);
        }
        init();
        while(m--)
        {

            int l,r=0;
            ans=0;
            scanf("%d",&ka);
            for(int i=0; i<ka; i++)
            {
                scanf("%d",&a[i]);
                r=max(r,deep[a[i]]);
            }
            scanf("%d",&kb);
            for(int i=0; i<kb; i++)
                scanf("%d",&b[i]);
            ans=solve(1,r);
             printf("%d\n",ans);
        }

    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值