hdu 5732 subway(2016 Multi-University Training Contest 1 1010)

9 篇文章 0 订阅
8 篇文章 0 订阅

题目链接

题目大意

给定两同构的树,要求输出配对的点对。

题解

树的同构。
对于树的同构问题,有两种解法,一种是利用Hash,另一种是利用树的最小表示法
对于Hash法可以看一下2007年国家集训队论文-杨弋《Hash 在信息学竞赛中的一类应用》。

首先对于两棵树如果同构,那么它们子树的顺序肯定不可以影响hash值。所以在计算一个点的hash值时,先把它的儿子的哈希值算出来,然后进行排序,再计算hash值。
由于每个点只会在父亲那里被排序一次,所以复杂度是 O(nlogn)
现在考虑如何设计hash函数。
我用了论文上讲的函数,也就是:

int sum=253771;
for(int i=0;i<t;i++)
    sum=(1LL*sum*A^D[i])%P;

也就是每次都乘上一个数,然后异或儿子的hash值,然后模P。
然而题目是无根树,并不可以找到两棵树的根。
可以利用树的中心,也就是直径的中点。
一棵树最多有两个中心,所以都为根匹配一下,最多匹配4次。
对于一棵子树,如果儿子结点数不同,肯定不匹配。
然后从小到大依次扫描儿子,如果不hash值不同,那么不匹配。
最后递归匹配每个儿子。
注意这里比较坑的就是递归的时候可能下一层把上一层的数组给覆盖掉了,导致答案错误等等。

#include <map>
#include <string>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
#pragma comment(linker, "/STACK:2048000000,2048000000")
using namespace std;
const int M=400005;
const int A=86373071;
const int P=1e7+3;
char ch[2][15];
char tree[M][15];
map<string,int>mp[2];
int sz;
struct Edge{
    int to,nxt;
    Edge(int a=0,int b=0):to(a),nxt(b){}
}edge[M];
int etot,head[M];
inline void add_edge(int a,int b){
    edge[etot]=Edge(b,head[a]);
    head[a]=etot++;
}
bool ans;
vector<int>son[M];
int B[M],hashfunction[M],C[M],match[M],D[M];
void Hashfunction(int x,int f){
    for(int i=head[x];~i;i=edge[i].nxt){
        int to=edge[i].to;
        if(to==f) continue;
        Hashfunction(to,x);
    }
    int t=0;
    for(int i=head[x];~i;i=edge[i].nxt)
        if(edge[i].to!=f) D[t++]=hashfunction[edge[i].to];
    sort(D,D+t);
    int sum=253771;
    for(int i=0;i<t;i++)
        sum=(1LL*sum*A^D[i])%P;
    hashfunction[x]=sum;
}
bool cmp(int a,int b){
    return hashfunction[a]<hashfunction[b];
}
void Match(int a,int b,int faa,int fab){
    match[a]=b;
    int t0=0,t1=0;
    for(int i=head[a];~i;i=edge[i].nxt){
        if(edge[i].to!=faa){
            B[t0++]=edge[i].to;
            son[a].push_back(edge[i].to);
        }
    }
    for(int i=head[b];~i;i=edge[i].nxt){
        if(edge[i].to!=fab){
            son[b].push_back(edge[i].to);
            C[t1++]=edge[i].to;
        }
    }
    if(t0!=t1){
        ans=0;
        return;
    }
    sort(son[a].begin(),son[a].end(),cmp);
    sort(son[b].begin(),son[b].end(),cmp);
    for(int i=0;i<t0;i++)
        if(hashfunction[son[a][i]]!=hashfunction[son[b][i]]){
            ans=0;
            return;
        }
    for(int i=0;i<t0;i++)
        Match(son[a][i],son[b][i],a,b);
}
int a[M],b[M];
int n,mx,id,fa[M],line[2][M];
void run(int a,int b){
    for(int i=1;i<=(n<<1);i++)
        son[i].clear();
    Hashfunction(a,0);
    Hashfunction(b,0);
    ans=1;
    Match(a,b,0,0);
}
void rec(int x,int len,int f){
    fa[x]=f;
    if(len>mx) mx=len,id=x;
    for(int i=head[x];~i;i=edge[i].nxt){
        int to=edge[i].to;
        if(to==f) continue;
        rec(to,len+1,x);
    }
}
void solve(){
    mp[0].clear();
    mp[1].clear();
    sz=etot=0;
    memset(head,-1,sizeof(head));
    for(int t=0;t<2;t++){
        for(int i=1;i<n;i++){
            int a[2];
            for(int j=0;j<2;j++){
                scanf("%s",ch[j]);
                if(mp[t].find(ch[j])==mp[t].end()){
                    mp[t][ch[j]]=++sz;
                    strcpy(tree[sz],ch[j]);
                }
                a[j]=mp[t][ch[j]];
            }
            add_edge(a[0],a[1]);
            add_edge(a[1],a[0]);
        }
    }
    int L1,R1,L2,R2;
    mx=-1;rec(1,0,0);L1=id;
    mx=-1;rec(id,0,0);R1=id;
    mx=-1;rec(n+1,0,0);L2=id;
    mx=-1;rec(id,0,0);R2=id;
    int x=R1,tot0=0,tot1=0;
    while(x!=L1) line[0][tot0++]=x,x=fa[x];
    line[0][tot0++]=x;
    x=R2;
    while(x!=L2) line[1][tot1++]=x,x=fa[x];
    line[1][tot1++]=x;
    // Hashfunction(1,0);
    // Hashfunction(n+1,0);
    if(tot0%2){
        run(line[0][tot0/2],line[1][tot1/2]);
        for(int i=1;i<=n;i++)
            printf("%s %s\n",tree[i],tree[match[i]]);
    }else{
        for(int i=-1;i<=0;i++)
            for(int j=-1;j<=0;j++){
                ans=1;
                run(line[0][tot0/2+i],line[1][tot1/2+j]);
                if(ans){
                    for(int k=1;k<=n;k++)
                        printf("%s %s\n",tree[k],tree[match[k]]);
                    return;
                }
            }
    }
}
int main(){
    while(scanf("%d",&n)!=EOF) solve();
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值