小蓝正在和朋友们团建,有一个游戏项目需要两人合作,两个人分别拿到一棵大小为 n 和 m 的树,树上的每个结点上有一个正整数权值。
两个人需要从各自树的根结点 1 出发走向某个叶结点,从根到这个叶结点的路径上经过的所有结点上的权值构成了一个正整数序列,两人的序列的最长公共前缀即为他们的得分。给出两棵树,请计算两个人最多的得分是多少。
输入的第一行包含两个正整数 n, m ,用一个空格分隔。
第二行包含 n 个正整数 c1, c2, · · · , cn ,相邻整数之间使用一个空格分隔,其中 ci 表示第一棵树结点 i 上的权值。
第三行包含 m 个正整数 d1, d2, · · · , dm ,相邻整数之间使用一个空格分隔,其中 di 表示第二棵树结点 i 上的权值。
接下来 n − 1 行,每行包含两个正整数 ui, vi 表示第一棵树中包含一条 ui 和vi 之间的边。
接下来 m − 1 行,每行包含两个正整数 pi, qi 表示第二棵树中包含一条 pi 和 qi 之间的边。
输出一行包含一个整数表示答案。
2 2
10 20
10 30
1 2
2 1
1
5 4
10 20 30 40 50
10 40 20 30
1 2
1 3
2 4
3 5
1 2
1 3
3 4
2
【样例1说明】两个序列可以为 [10, 20] , [10, 30] ,最大前缀为 1 。
【样例2说明】两个序列可以为 [10, 20, 40] , [10, 20, 30] ,最大前缀为 2 。
【评测用例规模与约定】
对于 20% 的评测用例,1 ≤ n, m ≤ 500 。
对于所有评测用例,1 ≤ n, m ≤ 2 × 10^5,1 ≤ ci, di ≤ 10^8 ,1 ≤ ui, vi ≤ n ,1 ≤ pi, qi ≤ m ,对于任意结点,其子结点的权重互不相同。
考虑深度优先搜索,从根结点开始,先构建一个从第一棵树子结点权值到边的映射,再遍历第二棵树的子结点,如果子结点的权值在第一棵树的映射中,则继续向下搜索。
#include <bits/stdc++.h>
#define int long long
#define endl '\n'
using namespace std;
int mmax = 0; // 最长公共前缀的最大长度
int dfs(int fn, int pn, int fm, int pm, int dep, const vector<int>& c, const vector<int>& d, const vector<vector<int>>& G, const vector<vector<int>>& T)
{
// fn是当前遍历的第一棵树的结点,pn是fn的父结点, fm是当前遍历的第二棵树的结点,pm是fm的父结点
mmax = max(mmax, dep);
map<int, int> mp;
for(auto t1 : G[fn])
{
if(t1 != pn)
{
mp[c[t1]] = t1;
}
}
for(auto t2 : T[fm])
{
if(t2 != pm)
{
if(mp.count(d[t2]) != 0)
{
mmax = max(mmax, dfs(mp[d[t2]], fn, t2, fm, dep + 1, c, d, G, T));
}
}
}
return mmax;
}
signed main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int n, m;
cin >> n >> m;
vector<int> weight1(n + 5), weight2(m + 5); // 各结点的权值
for(int i = 1; i <= n; i++)
{
cin >> weight1[i];
}
for(int i = 1; i <= m; i++)
{
cin >> weight2[i];
}
vector<vector<int>> tree1(n + 5), tree2(m + 5); // 两个结点之间的边(邻接表)
for(int i = 1; i < n; i++)
{
int u, v;
cin >> u >> v;
tree1[u].push_back(v);
tree1[v].push_back(u);
}
for(int i = 1; i < m; i++)
{
int u, v;
cin >> u >> v;
tree2[u].push_back(v);
tree2[v].push_back(u);
}
if(weight1[1] != weight2[1])
{
cout << 0 << endl;
}
else
{
int res = dfs(1, 0, 1, 0, 1, weight1, weight2, tree1, tree2);
cout << res << endl;
}
return 0;
}
1. 读入数据,构建两棵树各结点的权值数组的边的邻接表。
2. 如果两棵树根结点的权值不同,则最长公共前缀的长度一定为0(为了提升效率,可以不进行特判)。
3. 对两棵树同时从根节点进行深度优先搜索(暴力求解两棵树的dfs会TLE),将当前结点的子结点信息存入哈希表(邻接表中有当前结点与父结点的边,需要特判避免,防止重复访问已经访问过的节点导致无限循环),如果当前遍历的两个结点具有相同的权值(传参时保证当前结点的权值相同),并且它们的子结点也存在匹配的公共前缀,那么dfs函数会继续递归地遍历这两个结点的子结点,遍历的同时不断更新最长公共前缀的长度mmax。