hdu 6031 两集合间最大LCA

题目:

Innumerable Ancestors

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)
Total Submission(s): 598    Accepted Submission(s): 188


Problem Description
There is a tree having  n  nodes, labeled from 1 to  n . The root of the tree is always 1, and the depth of a node  p  is the number of nodes on the shortest path between node  p  and the root.
In computer science, the Lowest Common Ancestor (LCA) of two nodes  v  and  w  in a tree is the lowest (i.e. deepest) node that has both  v  and  w  as descendants, where we define each node to be a descendant of itself (so if  v  has a direct connection from  w w  is the lowest common ancestor).
You have to answer  m  queries. Each query gives two non-empty node sets  A  and  B , there might be some nodes in both sets.
You should select one node  x  from set  A , and one node  y  from set  B x  and  y  can be the same node. Your goal is to maximize the depth of the LCA of  x  and  y .
Please write a program to answer these queries.
 
Input
The input contains several test cases, no more than 5 test cases.
In each test case, the first line contains two integers  n(1n100000)  and  m(1m100000) , denoting the number of nodes and queries.
For the next  n1  lines,each line contians two integers  a  and  b , denoting a bi-directional edge between node  a  and  b .
Then there are  2m  lines, every two lines describes one query.
For each query, the first line describes the set A.
The first integer  k(1kn)  denotes the number of nodes in set  A , and the next  k  integers describing the nodes in set  A . There might be some nodes appear multiple times in the set.
The second line describes the set  B  in the same format of set  A .

It is guaranteed that  k100000  in each test case.
 
Output
For every query, print a number denoting the answer, which means the maximum depth of the LCA.
 
Sample Input
  
  
7 3 1 2 1 3 3 4 3 5 4 6 4 7 1 6 1 7 2 6 7 1 7 2 5 4 2 3 2
 
Sample Output
  
  
3 4 2


给一棵树,然后多次询问,每次询问给出两个集合,从集合A中选择一个点,从集合B中选择一个顶点,使得选出的两个顶点的LCA 的dep最大


#include <bits/stdc++.h>
using namespace std;

vector<int> g[105000];
int n,m,root;
int vs[205000];//dfs序
int dep[205000];//dfs序中结点所在层数
int id[105000];//每个结点在vs数组中的首次出现的下标
int f[205000][21];
int L[205000];//第i位存储i的log
struct node{
    int ty,x;//所在集合编号 结点下标
}rec[205000];///离线数组
bool operator<(const node& a,const node& b){
    return id[a.x]<id[b.x];//按照在DFS序中出现的先后次序进行排序
};
int tot;

void rmq_init(int tot){
    for(int i=0;i<tot;i++) f[i][0]=dep[i];
    for(int j=1;j<=20;j++){
        for(int i=0;i+(1<<j)-1<tot;i++){
            f[i][j]=min(f[i][j-1],f[i+(1<<(j-1))][j-1]);
        }
    }
}

inline void dfs(int v,int p,int d,int &k){
    id[v]=k;
    vs[k]=v;
    dep[k++]=d;
    for(int i=0;i<g[v].size();i++){
        if(g[v][i]!=p){
            dfs(g[v][i],v,d+1,k);
            vs[k]=v;
            dep[k++]=d;
        }
    }
}

void init(int V){
    int k=0;
    root=1;
    dfs(root,-1,0,k);//
    rmq_init(V*2-1);
}

inline int query(int a,int b){///返回LCA的dep
    int p=L[b-a+1];
    return min(f[a][p],f[b-(1<<p)+1][p]);
}

int main(){///1747MS	32600K
    for(int i=1;i<=200000;i++) L[i]=31-__builtin_clz(i);//011222233333333 相当于打印log(i) 上取整
    while(~scanf("%d%d",&n,&m)){
        for(int i=1;i<=n;i++) g[i].clear();
        for(int i=1;i<n;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            g[u].push_back(v);
            g[v].push_back(u);
        }
        init(n);
        //for(int i=0;i<2*n-1;i++)printf("%d ",vs[i]); printf("\n");
        //for(int i=0;i<2*n-1;i++)printf("%d ",dep[i]); printf("\n");
        //for(int i=1;i<=n;i++)printf("%d ",id[i]); printf("\n");
        while(m--){
            int ans=0;
            tot=0;
            int a;
            scanf("%d",&a);
            for(int i=1;i<=a;i++){
                int x;
                scanf("%d",&x);
                rec[++tot]=(node){1,x};
            }
            int b;
            scanf("%d",&b);
            for(int i=1;i<=b;i++){
                int x;
                scanf("%d",&x);
                rec[++tot]=(node){2,x};
            }
            sort(rec+1,rec+1+tot);///按id排个序,避免重复查询
            int am=-1,bm=-1;
            for(int i=1;i<=tot;i++){
                //printf("%d %d\n",rec[i].ty,rec[i].x);
                if(rec[i].ty==1){
                    am=id[rec[i].x];///当前集合中处理的元素在字典序中首次出现的位置
                    if(bm==-1) continue;///尚未遇到另一个集合中的元素
                    ans=max(ans,query(bm,am));
                    //printf("%d\n",query(am,id[rec[i].x]));
                }
                else {
                    bm=id[rec[i].x];
                    if(am==-1) continue;
                    ans=max(ans,query(am,bm));
                    //printf("%d %d %d\n",am,id[rec[i].x],query(am,id[rec[i].x]));
                }
            }
            printf("%d\n",ans+1);
        }
    }
}




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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值