CodeForces

题目链接

题目大意

  给你两棵树,让你找一个最大的集合,这个集合中的点在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;
}
原文地址:https://www.cnblogs.com/shuitiangong/p/15347113.html