Codeforces Round #722 (Div. 1) C. Trees of Tranquillity(dfs序,二分)
链接
题意:给你两棵
n
n
n个节点的树,要你找一个最大的点集
S
S
S,使得对于任意
u
,
v
∈
S
u,v\in S
u,v∈S,都满足
u
,
v
u,v
u,v在第一棵树上一个是另一个的祖先并且在第二棵树上互相都没有祖先关系,输出
S
S
S的大小。
思路:首先答案对于第一个树,一定是在一颗树上,而在第二个树上都不在同一条链上,换句话说,他们不存在哪个节点在另一个节点的子树中,我们利用dfs序,记录点子树的dfs序区间
[
l
,
r
]
[l,r]
[l,r],假设
i
i
i是
j
j
j的子节点,那么就有
l
[
i
]
≥
l
[
j
]
&
&
r
[
i
]
≤
r
[
j
]
l[i] \ge l[j]\&\&r[i] \le r[j]
l[i]≥l[j]&&r[i]≤r[j],我们就可以用一个set来维护,因为对于b中每一个被包含的区间,我们可以知道,假如一个区间被另一个区间包含,那么用这个区间对应的点来替换另一个点则一定可以容纳更多的点,而且一定是
l
l
l最大的区间可以满足可以包含现在的点,所以我们用二分找到这个区间,然后替换掉他,随着dfs第一颗树,一条链搜下去,每次用当前点更新set,set的大小就是集合
#include<bits/stdc++.h>
using namespace std;
const int N = 3e5+10;
set<int> st;
int l[N], r[N], ans, id[N], dfscnt, n;
vector<int> g[N], g1[N];
void dfsb(int u, int fa)
{
l[u] = ++dfscnt;
id[dfscnt] = u;
for (int i = 0; i < g1[u].size(); i++) {
int v = g1[u][i];
if (v == fa) continue;
dfsb(v, u);
}
r[u] = dfscnt;
}
int update(int u)
{
auto it = st.lower_bound(l[u]);
if (it != st.end() && r[id[*it]] <= r[u]) return -2;
if (it == st.begin()) return -1;
it--;
int res = id[*it];
if (r[u] > r[res]) return -1;
st.erase(it);return res;
}
void dfs(int u, int fa)
{
int res = update(u);
if (res != -2) {st.insert(l[u]);}
ans = max(ans, (int)st.size());
for (int i = 0; i < g[u].size(); i++) {
int v = g[u][i];
if (v == fa) continue;
dfs(v, u);
}
if (res != -2) {
st.erase(l[u]);
if (res != -1) {
st.insert(l[res]);
}
}
}
void solve()
{
cin >> n;st.clear();ans = 0;
for (int i = 1; i <= n; i++) {
g[i].clear(); g1[i].clear();
}
for (int i = 2; i <= n; i++) {
int v;
cin >> v;
g[i].push_back(v);
g[v].push_back(i);
}
for (int i = 2; i <= n; i++) {
int v;
cin >> v;
g1[i].push_back(v);
g1[v].push_back(i);
}
dfsb(1, -1);
dfs(1, -1);
cout << ans << endl;
}
int main()
{
int T;
cin >> T;
while (T--) {
solve();
}
return 0;
}