DESCRIPTION:
给出两棵节点数相同的树,求其是否同构,还要输出可行解(节点的匹配关系)带spj。
input
16(n个节点)
12 4
4 13
13 5
13 14
4 8
8 3
8 6
3 15
12 10
15 1
4 9
4 2
2 7
12 16
1 11
7 6
6 2
2 10
2 11
6 9
9 14
9 8
14 4
7 12
4 15
6 1
6 16
16 13
7 5
15 3
output
YES
15 16 14 6 10 8 13 9 1 12 3 7 2 11 4 5(p[i]表示树I中节点i对应树II中的节点p[i])。
SOLUTION:
Ⅰ给出的是无根树->找树的重心或中心,以之作根(O(n)->O(2))我找的树的中心,最多两个中心。
Ⅱ设定HASH函数,我设的是h(x) = Σ(h(x.son)) * soncnt;即其儿子的HASH函数之和乘上儿子的个数。
Ⅲ输出解:从两个树的根开始一起DFS,每找到一对HASH值相同的点,就将两个点的所有儿子分别加入两个优先队列,以HASH值为关键字维护。然后再一对一对的取出来(两个点HASH值相同代表两个点为根的子树一毛一样,所以随便匹配就好)用ans记录,然后递归DFS这对刚取出的新点。
CODE:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define ll long long int
const ll bv = 9973;
const ll mod = 1e9 + 7;
const int inf = 1 << 30;
const int maxn = 100000;
using namespace std;
struct node{
int v, hs;
node(){}
node(int a, int b){
v = a, hs = b;
}
bool operator < (const node& a)const{
return hs < a.hs;
}
};
struct Edge{
int v, Next;
}edge[maxn * 2 + 5][2];
int n, x, y, adj[maxn + 5][2], ecnt[2], f[maxn + 5], pa[maxn + 5];
int mid[3][3], ans[maxn + 5];
ll hv[maxn + 5];
int trv[maxn + 5][2];
bool mfg;
void HashPre()
{
hv[0] = 1;
for(int i = 1; i <= n; i ++){
hv[i] = hv[i - 1] * bv % mod;
}
}
void addedge(int u, int v, int fg)
{
edge[++ ecnt[fg]][fg].v = v;
edge[ecnt[fg]][fg].Next = adj[u][fg];
adj[u][fg] = ecnt[fg];
}
void dfs(int cur, int fa, int fg)
{
pa[cur] = fa;
for(int p = adj[cur][fg]; p; p = edge[p][fg].Next){
int v = edge[p][fg].v;
if(fa == v) continue;
f[v] = f[cur] + 1;
dfs(v, cur, fg);
}
}
void searchKP(int fg)//mid point
{
int maxx = 0, tr;
f[1] = 0;
dfs(1, -1, fg);
for(int i = 1; i <= n; i ++)
if(f[i] > maxx)
maxx = f[i], tr = i;
f[tr] = 0;
dfs(tr, -1, fg);
maxx = 0;
int tr2;
for(int i = 1; i <= n; i ++)
if(f[i] > maxx)
maxx = f[i], tr2 = i;
int Mid = tr2, last;
while(f[pa[Mid]] >= maxx / 2)
last = Mid, Mid = pa[Mid];
mid[fg][0] = Mid;
if(maxx & 1)//2 midpoints
mfg = 1, mid[fg][1] = last;
}
ll slv(int rt, int tfg, int fa)
{
ll ret = 1;
int soncnt = 1;
for(int p = adj[rt][tfg]; p; p = edge[p][tfg].Next){
int v = edge[p][tfg].v;
if(fa == v) continue;
ret += slv(v, tfg, rt);
if(ret >= mod) ret -= mod;
soncnt ++;
}
return trv[rt][tfg] = ret * hv[soncnt] % mod;
}
void DfsAns(int r1, int fa1, int r2, int fa2)
{
priority_queue<node> q1, q2;//match the point
for(int p = adj[r1][0]; p; p = edge[p][0].Next){
int v = edge[p][0].v;
if(fa1 == v) continue;
q1.push(node(v, trv[v][0]));
}
for(int p = adj[r2][1]; p; p = edge[p][1].Next){
int v = edge[p][1].v;
if(fa2 == v) continue;
q2.push(node(v, trv[v][1]));
}
while(!q1.empty()){
node cur1 = q1.top(), cur2 = q2.top();
q1.pop(), q2.pop();
ans[cur1.v] = cur2.v;
DfsAns(cur1.v, r1, cur2.v, r2);
}
}
void PrintAns(int m1, int m2)
{
ans[m1] = m2;
DfsAns(m1, -1, m2, -1);
for(int i = 1; i < n; i ++)
printf("%d ", ans[i]);
printf("%d", ans[n]);
}
int main()
{
scanf("%d", &n);
HashPre();
for(int i = 1; i < n; i ++){
scanf("%d%d", &x, &y);
addedge(x, y, 0);
addedge(y, x, 0);
}
for(int i = 1; i < n; i ++){
scanf("%d%d", &x, &y);
addedge(x, y, 1);
addedge(y, x, 1);
}
searchKP(0);
searchKP(1);
ll h1 = slv(mid[0][0], 0, -1);
ll h2 = slv(mid[1][0], 1, -1);
if(h1 == h2){
puts("YES");
PrintAns(mid[0][0], mid[1][0]);
return 0;
}
if(mfg){//try another midpoint
h1 = slv(mid[0][1], 0, -1);
if(h1 == h2){
puts("YES");
PrintAns(mid[0][1], mid[1][0]);
return 0;
}
}
puts("NO");
return 0;
}