题目大意
给你两棵树,让你找一个最大的集合,这个集合中的点在A树中每一个都与其他点是父亲或者儿子的关系,在B树中每一个点和其他点都没有父子关系。
解题思路
容易发现,满足第一个要求的点集在A树的一条链上,而对于第二个要求,可以求出B树的欧拉序,两个点没有父子关系,也就是两个点的欧拉序没有交集(欧拉序只有包含和完全不相交两种,所以判包含即可),贪心选一个最大的不相交集合即可。
有两种情况。第一种是当前是大区间,包含了之前的小区间,因为题目给出了(a_i, b_i < i),所以肯定是大区间在前,小区间在后,所以没有这种情况。第二种是当前是小区间,之前有大区间包含他,那肯定舍弃之前的大区间最优。使用set来存储欧拉序的左端点,然后每次找出比当前点左端点小的那个左端点,判断其对应的右端点是否包含当前的区间,如果包含,那么就舍弃之前的点。以为有先访问在区间再访问小区间和欧拉序的性质,所以每次加入当前的点肯定是最优的,就看是否要舍弃之前的点了。
代码
const int maxn = 3e5+10;
vector<int> e[maxn], g[maxn];
int n, l[maxn], r[maxn], id[maxn], idx, ans;
void dfs1(int u) {
l[u] = ++idx, id[idx] = u;
for (auto v : g[u]) dfs1(v);
r[u] = idx;
}
set<int> st;
void dfs2(int u) {
vector<int> del;
auto it = st.lower_bound(l[u]);
--it;
if (*it>0 && r[id[*it]]>=l[u]) {
del.push_back(*it);
st.erase(it);
}
st.insert(l[u]);
ans = max(ans, (int)st.size()-3);
for (auto v : e[u]) dfs2(v);
st.erase(l[u]);
if (del.size()) st.insert(del[0]);
}
void init() {
for (int i = 1; i<=n; ++i) e[i].clear(), g[i].clear();
st.clear(); idx = ans = 0;
}
int main() {
IOS;
int __; cin >> __;
while(__--) {
cin >> n; init();
for (int i = 2, a; i<=n; ++i) {
cin >> a;
e[a].push_back(i);
}
for (int i = 2, a; i<=n; ++i) {
cin >> a;
g[a].push_back(i);
}
dfs1(1);
st.insert(INF); st.insert(-INF); st.insert(-INF-1);
dfs2(1);
cout << ans << endl;
}
return 0;
}