Codeforces Round #722 (Div. 1) / CF1528C Trees of Tranquillity(DFS序)
Soroush and Keshi each have a labeled and rooted tree on 𝑛n vertices. Both of their trees are rooted from vertex 11.
Soroush and Keshi used to be at war. After endless decades of fighting, they finally became allies to prepare a Codeforces round. To celebrate this fortunate event, they decided to make a memorial graph on 𝑛n vertices.
They add an edge between vertices 𝑢u and 𝑣v in the memorial graph if both of the following conditions hold:
- One of 𝑢u or 𝑣v is the ancestor of the other in Soroush's tree.
- Neither of 𝑢u or 𝑣v is the ancestor of the other in Keshi's tree.
Here vertex 𝑢u is considered ancestor of vertex 𝑣v, if 𝑢u lies on the path from 11 (the root) to the 𝑣v.
Popping out of nowhere, Mashtali tried to find the maximum clique in the memorial graph for no reason. He failed because the graph was too big.
Help Mashtali by finding the size of the maximum clique in the memorial graph.
As a reminder, clique is a subset of vertices of the graph, each two of which are connected by an edge.
Input
The first line contains an integer 𝑡t (1≤𝑡≤3⋅105)(1≤t≤3⋅105) — the number of test cases. The description of the test cases follows.
The first line of each test case contains an integer 𝑛n (2≤𝑛≤3⋅105)(2≤n≤3⋅105).
The second line of each test case contains 𝑛−1n−1 integers 𝑎2,…,𝑎𝑛a2,…,an (1≤𝑎𝑖<𝑖)(1≤ai<i), 𝑎𝑖ai being the parent of the vertex 𝑖i in Soroush's tree.
The third line of each test case contains 𝑛−1n−1 integers 𝑏2,…,𝑏𝑛b2,…,bn (1≤𝑏𝑖<𝑖)(1≤bi<i), 𝑏𝑖bi being the parent of the vertex 𝑖i in Keshi's tree.
It is guaranteed that the given graphs are trees.
It is guaranteed that the sum of 𝑛n over all test cases doesn't exceed 3⋅1053⋅105.
Output
For each test case print a single integer — the size of the maximum clique in the memorial graph.
Example
input
Copy
4
4
1 2 3
1 2 3
5
1 2 3 4
1 1 1 1
6
1 1 1 1 2
1 2 1 2 2
7
1 1 3 4 4 5
1 2 1 4 2 5
output
Copy
1
4
1
3
这个题翻译过来的大致意思是给定两棵编号分别为1~n的树,让你从第一棵树的一条根到叶子的链中选取若干个点,使得这些点对应的编号在第二棵树中对应的点不互为祖先。
考虑使用dfs序来判断是否为祖先。先对第二棵树dfs求出每个点的dfs序存入dfn数组,此时同时求出每个点为根的子树的dfn值得范围。此时不互为祖先这个条件等价于选出的点对应的范围两两没有交集(可以自己画棵树模拟一下)。同时注意到区间的性质,这些区间实际上是包含关系(类似合法括号序列),因此对于对答案的贡献而言一个小的区间一定比一个大的区间不劣。因此直接对第一棵树dfs,每到一个点考虑将这个点加入答案集合,这是需要用一个数据结构维护区间的关系。如果这个点对应的第二棵树的dfn区间被覆盖了,则将原来覆盖的区间删除,将当前点对应的区间加入;反之说明不冲突可以直接加入。最后统计数据结构中存储的区间个数即可(留下的必然亮亮不相交)。注意dfs时需要回溯。因为每个区间左端点唯一对应于一个右端点,因此维护区间可以选择set,set中存储的是区间左端点(每个左端点对应的右端点在第一次dfs的时候可以预处理出来),判断是否发生覆盖可以在set中二分找到第一个小于当前区间左端点的左端点,如果这个之前的左端点对应的右端点大于等于当前区间的右端点说明发生了覆盖。
代码中还有一个小小的最优性剪枝,如果目前答案集合的区间个数 + 当前点对应的子树的最大深度(可以预处理) 不大于已经搜索到的答案的话则直接返回。加上这个剪枝时间大约能快100ms(虽然没什么很大作用)。
注意对于set二分需要使用set自己的upper_bound,否则会超时
#include <bits/stdc++.h>
#define N 300005
#define int long long
#define LL long long
using namespace std;
int head1[N], ver1[2 * N], Next1[2 * N], tot1 = 0;
int head2[N], ver2[2 * N], Next2[2 * N], tot2 = 0;
int n;
void add1(int x, int y) {
ver1[++tot1] = y, Next1[tot1] = head1[x], head1[x] = tot1;
}
void add2(int x, int y) {
ver2[++tot2] = y, Next2[tot2] = head2[x], head2[x] = tot2;
}
int id = 0;//获得第二棵树每个点dfn值的变量
int dfn[N];
int lft[N], rt[N];//对应子树的dfn区间的左右端点
int lr[N];//因为每个左端点唯一对应于一个右端点 因此lr[i]表示以i为左端点的区间对应的右端点
int dep[N];//第一棵树上各个节点的深度,方便剪枝
int max_subtree_dep[N];//子树中的节点能到达的最大深度
void getdep(int x, int pre, int d) {
dep[x] = max_subtree_dep[x] = d;
for(int i = head1[x]; i; i = Next1[i]) {
int y = ver1[i];
if(y == pre) continue;
getdep(y, x, d + 1);
max_subtree_dep[x] = max(max_subtree_dep[x], max_subtree_dep[y]);
}
}
void dfs1(int x, int pre) {
dfn[x] = ++id;
lft[x] = dfn[x];
for(int i = head2[x]; i; i = Next2[i]) {
int y = ver2[i];
if(y == pre) continue;
dfs1(y, x);
}
rt[x] = id;
lr[lft[x]] = rt[x];
}
set<int, greater<int>> st;
int ans;
void dfs2(int x, int pre) {
ans = max(ans, (int)st.size());
if((int)st.size() + max_subtree_dep[x] - dep[x] <= ans) return;//剪
for(int i = head1[x]; i; i = Next1[i]) {
int y = ver1[i];
if(y == pre) continue;
if(!st.size()) {
st.insert(lft[y]);
dfs2(y, x);
st.erase(lft[y]);
} else {
set<int>::iterator it = st.upper_bound(lft[y]);//找到第一个小于lft[y]的元素
int tmp = *it;//左端点
if(it != st.end() && lr[tmp] >= rt[y]) {//包含
st.erase(tmp);
st.insert(lft[y]);
int pre = (*it);
dfs2(y, x);
st.erase(lft[y]);
st.insert(tmp);
} else {
st.insert(lft[y]);
dfs2(y, x);
st.erase(lft[y]);
}
}
}
}
signed main() {
cin.tie(0);
ios::sync_with_stdio(0);
int t;
cin >> t;
while(t--) {
tot1 = tot2 = 0;
for(int i = 1; i <= n; i++) head1[i] = head2[i] = 0;
id = 0;
ans = 0;
st.clear();
cin >> n;
for(int i = 2; i <= n; i++) {
int tmp;
cin >> tmp;
add1(i, tmp);
add1(tmp, i);
}
for(int i = 2; i <= n; i++) {
int tmp;
cin >> tmp;
add2(i, tmp);
add2(tmp, i);
}
dfs1(1, 0);
st.insert(lft[1]);
getdep(1, 0, 1);
dfs2(1, 0);
cout << ans << endl;
}
return 0;
}