题目链接 2016多校1 Problem J
题意 给定两棵相同的树,但是编号方案不同。求第一棵树上的每个点对应的第二棵树上的点。输出一种方案即可。
首先确定树的直径的中点。两棵树相等意味着两棵树的直径相等。
然而直径有很多条,我们任意求出两棵树的各一条直径并不以为着这两条直径是相对应的。
但是直径的中点一定是相对应的。
确定根结点后对整棵树哈希并进行匹配的这个过程是不难的。
当直径长度为偶数时中点有两个,那么最多做$4$次匹配就可以了。
这个哈希函数要好好设计,很容易产生冲突。
#include <bits/stdc++.h>
using namespace std;
#define rep(i, a, b) for (int i(a); i <= (b); ++i)
#define dec(i, a, b) for (int i(a); i >= (b); --i)
typedef unsigned long long LL;
const LL A = 90918567;
const LL B = 87378051;
const LL mod = 1e9 + 7;
const int N = 1e5 + 10;
int a[N], ans[N], father[N], roota[3], rootb[3];
int numa, numb, n, x, y, c1, c2, L, R, cnt;
bool flag;
map <string, int> mp1, mp2;
vector <int> v[N], g[N];
string ss[N], tt[N], s1, s2;
LL c[N], f[N];
void init(){
mp1.clear();
mp2.clear();
rep(i, 0, n + 1) v[i].clear(), g[i].clear();
c1 = 0, c2 = 0;
}
int get1(string s){
if (mp1.count(s)) return mp1[s];
mp1[s] = ++c1;
ss[c1] = s;
return c1;
}
int get2(string s){
if (mp2.count(s)) return mp2[s];
mp2[s] = ++c2;
tt[c2] = s;
return c2;
}
void dfs1(int x, int fa, int now, vector <int> v[N]){
if (now > c1){
c1 = now;
L = x;
}
for (auto u : v[x]){
if (u == fa) continue;
dfs1(u, x, now + 1, v);
}
}
void dfs2(int x, int fa, int now, vector <int> v[N]){
father[x] = fa;
if (now > c2){
c2 = now;
R = x;
}
for (auto u : v[x]){
if (u == fa) continue;
dfs2(u, x, now + 1, v);
}
}
void gethash_a(int x, int fa){
vector <LL> val;
for (auto u : v[x]){
if (u == fa) continue;
gethash_a(u, x);
val.push_back(c[u]);
}
sort(val.begin(), val.end());
c[x] = B;
for (auto u : val){
c[x] = (c[x] * A ^ u) % mod;
}
}
void gethash_b(int x, int fa){
vector <LL> val;
for (auto u : g[x]){
if (u == fa) continue;
gethash_b(u, x);
val.push_back(f[u]);
}
sort(val.begin(), val.end());
f[x] = B;
for (auto u : val){
f[x] = (f[x] * A ^ u) % mod;
}
}
bool cmp_a(const int &a, const int &b){
return c[a] < c[b];
}
bool cmp_b(const int &a, const int &b){
return f[a] < f[b];
}
void work(int x, int y, int fa_a, int fa_b){
vector <int> na, nb;
for (auto u : v[x]) if (u != fa_a) na.push_back(u);
for (auto u : g[y]) if (u != fa_b) nb.push_back(u);
if ((int)na.size() != (int)nb.size()){
return;
}
sort(na.begin(), na.end(), cmp_a);
sort(nb.begin(), nb.end(), cmp_b);
int sz = (int)na.size();
rep(i, 0, sz - 1) if (c[na[i]] == f[nb[i]]) ans[na[i]] = nb[i];
rep(i, 0, sz - 1){
int u1 = na[i], u2 = nb[i];
work(u1, u2, x, y);
}
}
bool solve(int roota, int rootb){
gethash_a(roota, 0);
gethash_b(rootb, 0);
memset(ans, -1, sizeof ans);
if (c[roota] != f[rootb]) return false;
ans[roota] = rootb;
work(roota, rootb, 0, 0);
bool ret = true;
rep(i, 1, n) if (ans[i] == -1){
ret = false;
break;
}
return ret;
}
void print(){
rep(i, 1, n) cout << ss[i] << " " << tt[ans[i]] << endl;
}
int main(){
ios::sync_with_stdio(false);
while (cin >> n){
init();
rep(i, 2, n){
cin >> s1 >> s2;
x = get1(s1);
y = get1(s2);
v[x].push_back(y);
v[y].push_back(x);
}
rep(i, 2, n){
cin >> s1 >> s2;
x = get2(s1);
y = get2(s2);
g[x].push_back(y);
g[y].push_back(x);
}
L = 0, R = 0;
c1 = 0, c2 = 0;
dfs1(1, 0, 0, v);
memset(father, 0, sizeof father);
dfs2(L, 0, 0, v);
x = R;
cnt = 0;
numa = 0;
while (true){
a[++cnt] = R;
R = father[R];
if (R == 0) break;
}
if (cnt & 1) roota[++numa] = a[(cnt + 1) / 2];
else{
roota[++numa] = a[cnt / 2];
roota[++numa] = a[cnt / 2 + 1];
}
L = 0, R = 0;
c1 = 0, c2 = 0;
dfs1(1, 0, 0, g);
memset(father, 0, sizeof father);
dfs2(L, 0, 0, g);
x = R;
cnt = 0;
numb = 0;
while (true){
a[++cnt] = R;
R = father[R];
if (R == 0) break;
}
if (cnt & 1) rootb[++numb] = a[(cnt + 1) / 2];
else{
rootb[++numb] = a[cnt / 2];
rootb[++numb] = a[cnt / 2 + 1];
}
flag = false;
rep(i, 1, numa){
rep(j, 1, numb){
if (solve(roota[i], rootb[j])){
flag = true;
print();
break;
}
}
if (flag) break;
}
}
return 0;
}