Codeforces Round #722 (Div. 1) C. Trees of Tranquillity(dfs序,二分)

Codeforces Round #722 (Div. 1) C. Trees of Tranquillity(dfs序,二分)

链接
题意:给你两棵 n n n个节点的树,要你找一个最大的点集 S S S,使得对于任意 u , v ∈ S u,v\in S u,vS,都满足 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;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值