1 solutions

  • 0
    @ 2025-4-29 20:51:18

    我们考虑如果一颗子树是阴阳子树,那么这颗子树能够产生贡献的时间区间段是多少 我们考虑 dpdp 出子树 uu 中最早变成斑马子树的时间戳,最晚变成非斑马子树的时间戳 rr 那么 [l,r][l,r] 的时间段上 uu 的贡献一直为 11 ,我们考虑差分,最后前缀和统计答案即可

    #include <iostream>
    #include <vector>
    using namespace std;
    
    const int N = 2e5 + 10;  // 题目最大节点数(n≤2e5)
    
    int n;
    int t[N];        // t[u]:节点u被染色的时间(第i次操作染p[i],时间i从1到n)
    int mi[N], mx[N];// mi[u]/mx[u]:以u为根的子树中最小/最大时间戳
    int ans[N];      // 差分数组,用于统计每个时间点的阴阳子树数量
    vector<int> g[N];// 树的邻接表
    
    // DFS遍历,计算每个子树的mi和mx(后序遍历)
    void dfs(int u, int par) {
        mi[u] = mx[u] = t[u];  // 初始化为当前节点的时间戳
        for (int v : g[u]) {
            if (v == par) continue;  // 跳过父节点
            dfs(v, u);
            mi[u] = min(mi[u], mi[v]);  // 合并子树的mi
            mx[u] = max(mx[u], mx[v]);  // 合并子树的mx
        }
        // 差分操作:当前子树的时间区间是 [mi[u], mx[u]]
        ans[mi[u]]++;
        ans[mx[u]]--;
    }
    
    void solve() {
        cin >> n;
        // 构建树(节点2~n的父节点)
        for (int i = 2; i <= n; ++i) {
            int u;
            cin >> u;
            g[u].push_back(i);  // 无向边(邻接表)
            g[i].push_back(u);
        }
        // 读取染色顺序p,并记录每个节点的染色时间t[u]
        for (int i = 1; i <= n; ++i) {
            int u;  // 第i次操作染节点u
            cin >> u;
            t[u] = i;  // 时间从1到n
        }
        // 从根节点1开始DFS
        dfs(1, 0);
        // 计算前缀和,得到每个时间点的阴阳子树数量
        int current = 0;
        for (int i = 1; i <= n; ++i) {
            current += ans[i];
            cout << current << " \n"[i == n];  // 最后一个数后换行
        }
    }
    
    int main() {
        ios::sync_with_stdio(false);  // 加速输入输出
        cin.tie(nullptr);
        solve();
        return 0;
    }
    
    • 1

    Information

    ID
    1464
    Time
    2000ms
    Memory
    256MiB
    Difficulty
    7
    Tags
    (None)
    # Submissions
    25
    Accepted
    8
    Uploaded By