2021牛客暑期多校训练营7

2021牛客暑期多校训练营7_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ

F - xay loves trees

本题有很多种做法,因为条件一要求第一颗树选出来的点连续,所以这里用了树上滑动窗口的方法,具体地如下:

\(\bullet\) 首先求出第二棵树的括号化序列,即对第二棵树进行\(dfs\),保存每个节点\(u\)进入时间\(L_u\)和退出的时间\(R_u\),那么节点\(u\)就影响了一个\([L_u,R_u]\)的区间,如果选了\(u\),那么其他节点的区间都不能和\([L_u,R_u]\)有交点,括号化序列有一个性质,如果\(u\)\(v\)的一个祖先,那么\(L_u < L_v ~\&\& ~R_v<R_u\),就是说\(v\)影响的区间会被\(u\)的包括,那么本题就变成了从第一棵树中找到一条连续的链满足所有区间互不相交。

\(\bullet\) 我们遍历第一棵树的所有链并保存在一个双端队列\(link\)中,并且维护一个区间的集合\(S\)\(dfs\)遍历这颗树,如果当前遍历到的点\(u\)和集合内的区间有交点,我们就不断弹出\(link\)的头节点,并删除头结点在集合\(S\)中的区间,不断操作直到满足条件,因为树上链有很多,所以我们还要进行回溯操作,那么这些删除的节点回溯后还是要用到,所以我们要将删除的节点也保存下来并在回溯的时候重新加入\(link\)中。

#include <bits/stdc++.h>
using namespace std;

const int MAXN = 3e5 + 5;

vector<int> e[3][MAXN];
int idx, L[MAXN << 1], R[MAXN << 1];
void dfs2(int u, int fa, int rt) {
    L[u] = ++idx;
    for (int v: e[rt][u]) {
        if (v == fa) continue;
        dfs2(v, u, rt);
    }
    R[u] = ++idx;
}

int res;
deque<int> link; // 保存这条链的点
set<pair<int, int>> S; // <st[i], i>
/*

             L[x]       R[x]
             --------------
----------                      --------------
L[v]  R[v]                      L[u]      R[u]

*/
// 判断插入节点x的区间后是否会和集合有交点
bool check(int x) {
    // 集合为空特判
    if (S.empty()) return true;
    // 这里lower_bound也行
    auto it = S.upper_bound({L[x], x});
    // 没有比它大的
    if (it == S.end()) {
        int v = (--it) -> second;
        return R[v] < L[x];
    }
    // 如果第一个就比它大
    if (it == S.begin()) {
        int u = it -> second;
        return R[x] < L[u];
    }
    // 否则比较两边
    int u = it -> second, v = (--it) -> second;
    return R[v] < L[x] && R[x] < L[u];
}
void dfs1(int u, int fa, int rt) {
    vector<int> V; // 保存删除的点
    // 如果当前节点和区间集合有交点则不断弹出链上的头节点并做记录方便后面回溯
    while (!check(u)) {
        int x = link.front();
        link.pop_front();   // 弹出这条链的头元素
        V.push_back(x);     // 并用V保存
        S.erase({L[x], x}); // 同时在S中删除
    }
    link.push_back(u);      // 插入当前节点
    S.insert({L[u], u});
    res = max(res, (int)S.size());
    for (int v: e[rt][u]) {
        if (v == fa) continue;
        dfs1(v, u, rt);
    }
    // 回溯
    link.pop_back();
    S.erase({L[u], u});
    while (!V.empty()) {
        int x = V.back();
        V.pop_back();
        link.push_front(x);
        S.insert({L[x], x});
    }
}

int main(int argc, char *argv[]) {
    int T;
    scanf("%d", &T);
    while (T--) {
        S.clear();
        idx = res = 0;
        int n;
        scanf("%d", &n);
        for (int i = 1; i <= n; ++i) {
            e[1][i].clear();
            e[2][i].clear();
        }
        for (int rt = 1; rt <= 2; ++rt) {
            for (int i = 1; i < n; ++i) {
                int u, v;
                scanf("%d %d", &u, &v);
                e[rt][u].push_back(v);
                e[rt][v].push_back(u);
            }
        }
        dfs2(1, 0, 2);
        dfs1(1, 0, 1);
        printf("%d\n", res);
    }
    system("pause");
    return 0;
}
posted @ 2021-08-30 16:50  stler  阅读(60)  评论(0)    收藏  举报