您的位置:首页 > 大数据 > 人工智能

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

2016-07-23 16:06 288 查看
题目链接

题目大意

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

题解

树的同构。

对于树的同构问题,有两种解法,一种是利用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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  树的同构 hdu 多校