HDU 4409 Family Name List(LCA)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4409

题意:给定一个家谱。有三种询问:

(1)L重新打印家谱,同一代的人按照升序;

(2)b name  输出name的亲兄弟有多少个?

(3)c name1 name2输出name1和name2 的最近公共祖先。

注意(3):如果最近公共祖先是name1或者name2,那么要输出其父亲。(因为自己不能是自己的祖先。。)

 

思路:建图的时候就按照正常的建,但是输出的时候将同一个父亲的孩子排序输出(2)在DFS的时候顺便统计一下;(3)就是LCA。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;


const int MAX=30005;  //节点数量
struct node
{
    int v,next;
};

struct Node
{
    char s[65];
    int id;
};

int cmp(Node a,Node b)
{
    return strcmp(a.s,b.s)<0;
}


node edges[MAX<<1];
int head[MAX],e;
int pre[MAX],dep[MAX];
int id[MAX]; //节点第一次被遍历的顺序
int RMQ[MAX*3][20];
int curID;
//F[i]表示第i个遍历的节点
//B[i]表示F[i]在树中的深度
int F[MAX*3],B[MAX*3];

int father[MAX],d[MAX];
int ID;
Node hash[MAX],hash1[MAX];

int add(char str[])
{
    ID++;
    strcpy(hash[ID].s,str);
    hash[ID].id=ID;
    return ID;
}

int getID(char str[])
{
    int low=1,high=ID,mid;
    while(low<=high)
    {
        mid=(low+high)>>1;
        if(strcmp(hash[mid].s,str)==0) break;
        if(strcmp(hash[mid].s,str)<0) low=mid+1;
        else high=mid-1;
    }
    return hash[mid].id;
}

void init()
{
    curID=0;
    ID=0;
    memset(head,-1,sizeof(head));
    e=0;
}

void Add(int u,int v)
{
    edges[e].v=v;
    edges[e].next=head[u];
    head[u]=e++;
}

int sonNum[MAX];

void DFS(int u,int p,int Dep)
{
    int i,v;
    
    curID++;
    F[curID]=u;  B[curID]=Dep;
    id[u]=curID; pre[u]=p; dep[u]=Dep;
    
    sonNum[u]=0;
    for(i=head[u];i!=-1;i=edges[i].next)
    {
        v=edges[i].v;
        if(v==p) continue;
        DFS(v,u,Dep+1);
        curID++;
        F[curID]=u;
        B[curID]=Dep;
        sonNum[u]++;
    }
}

void initRMQ()
{
    int i,j,x,y;
    for(i=1;i<=curID;i++) RMQ[i][0]=i;
    for(j=1;(1<<j)<=curID;j++) for(i=1;i+(1<<j)-1<=curID;i++)
    {
        x=RMQ[i][j-1];
        y=RMQ[i+(1<<(j-1))][j-1];
        RMQ[i][j]=B[x]<B[y]?x:y;
    }
}

int getLCA(int a,int b)
{
    int k,x,y;
    a=id[a];b=id[b];
    if(a>b) k=a,a=b,b=k;
    k=log(1.0+b-a)/log(2.0);
    x=RMQ[a][k];
    y=RMQ[b-(1<<k)+1][k];
    return B[x]<B[y]?F[x]:F[y];
}

void build(int n)
{
    char str[65];
    int i,j,k,u,v;
    
    scanf("%s",str);
    u=add(str);
    d[u]=0;father[u]=-1;
    for(i=2;i<=n;i++)
    {
        scanf("%s",str);
        for(j=0;str[j]=='.';j++);
        for(k=j;str[k];k++) str[k-j]=str[k];
        str[k-j]='\0';
        v=add(str);
        d[v]=j;
        while(d[u]+1!=d[v]) u=father[u];
        Add(u,v);
        father[v]=u;
        u=v;
    }
}

int temp[MAX],start[MAX],size[MAX];
void print(int u,int dep)
{
    int i,j,v;
    for(i=1;i<=dep;i++) putchar('.');
    printf("%s\n",hash1[u].s);
    for(i=start[u];i<start[u]+size[u];i++)
    {
        j=temp[i];
        v=edges[j].v;
        print(v,dep+1);
    }
}


int cmp1(int a,int b)
{
    int v1=edges[a].v;
    int v2=edges[b].v;
    return strcmp(hash1[v1].s,hash1[v2].s)<0;
}

void deal(int Q)
{
    char cmd[5],str[65],str1[65];
    int i,j,u,v;
    for(i=1;i<=ID;i++) hash1[i]=hash[i];
    sort(hash+1,hash+ID+1,cmp);
    u=1;
    for(i=1;i<=ID;i++)
    {
        size[i]=0;
        start[i]=u;
        for(j=head[i];j!=-1;j=edges[j].next)
        {
            temp[u]=j;
            size[i]++;
            u++;
        }
        if(size[i])
        {
            sort(temp+start[i],temp+start[i]+size[i],cmp1);
        }
    }
    while(Q--)
    {
        scanf("%s",cmd);
        if(cmd[0]=='L')
        {
            print(1,0);
        }
        else if(cmd[0]=='b')
        {
            scanf("%s",str);
            u=getID(str);
            if(father[u]==-1) puts("1");
            else printf("%d\n",sonNum[father[u]]);
        }
        else if(cmd[0]=='c')
        {
            scanf("%s%s",str,str1);
            u=getID(str);
            v=getID(str1);
            i=getLCA(u,v);
            if(i==u||i==v) i=father[i];
            printf("%s\n",hash1[i].s);
        }
    }
}




int n,Q;

int main()
{
    while(scanf("%d",&n),n)
    {
        init();
        build(n);
        DFS(1,0,0);
        initRMQ();
        scanf("%d",&Q);
        deal(Q);
    }
    return 0;
}

  

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值