1 条题解

  • 0
    @ 2026-10-26 19:23:52

    思路与算法

    把控制集合表示成以虚根0为根的祖先链。依编号建立新人的父亲为其全部受访对象的LCA;空对象集合直接连虚根。查询将给定点按DFS序排序去重,祖先链并集大小为Σdepth(v_i)-Σi>1 depth(LCA(v_{i-1},v_i))。

    正确性说明

    若此前每个控制集合为祖先链,则其交集正是各点LCA的祖先链,新人自身加入后接到LCA下方,归纳成立。空集合依公开样例不视为所有人。DFS序相邻链的交叠恰好覆盖此前并集与当前链的共有前缀,逐条加入的新增长度是深度差,因此公式准确。

    复杂度

    设输入对象总数B、查询元素总数S。建树和LCA O((N+B)logN),查询排序Σs logs、LCA O(SlogN),空间O(NlogN+最大查询大小),初始化包含祖先表。

    易错点

    虚根不是真人,深度从0计算。重复查询点先去重。不可按原图一般可达性求并集,本题是所有对象控制集合的交;父亲依更早编号在线确定。

    参考代码(C++11)

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n;
        cin >> n;
        int K = 1;
        while ((1 << K) <= n)
            K++;
        vector<vector<int>> up(K, vector<int>(n + 1)), g(n + 1);
        vector<int> dep(n + 1), tin(n + 1);
        auto lca = [&](int a, int b) {
            if (dep[a] < dep[b])
                swap(a, b);
            for (int k = K - 1; k >= 0; k--)
                if ((dep[a] - dep[b]) >> k & 1)
                    a = up[k][a];
            if (a != b) {
                for (int k = K - 1; k >= 0; k--)
                    if (up[k][a] != up[k][b]) {
                        a = up[k][a];
                        b = up[k][b];
                    }
                a = up[0][a];
            }
            return a;
        };
        for (int i = 1; i <= n; i++) {
            int sz, p = -1;
            cin >> sz;
            while (sz--) {
                int v;
                cin >> v;
                p = p < 0 ? v : lca(p, v);
            }
            if (p < 0)
                p = 0;
            up[0][i] = p;
            dep[i] = dep[p] + 1;
            g[p].push_back(i);
            for (int k = 1; k < K; k++)
                up[k][i] = up[k - 1][up[k - 1][i]];
        }
        vector<int> st(1, 0);
        int timer = 0;
        while (!st.empty()) {
            int u = st.back();
            st.pop_back();
            tin[u] = timer++;
            for (int v : g[u])
                st.push_back(v);
        }
        int q;
        cin >> q;
        while (q--) {
            int k;
            cin >> k;
            vector<int> a(k);
            for (int &x : a)
                cin >> x;
            sort(a.begin(), a.end(), [&](int x, int y) {
                return tin[x] < tin[y];
            });
            a.erase(unique(a.begin(), a.end()), a.end());
            long long ans = 0;
            for (int i = 0; i < (int)a.size(); i++)
                ans += dep[a[i]] - (i ? dep[lca(a[i - 1], a[i])] : 0);
            cout << ans << '\n';
        }
    }
    
    • 1

    信息

    ID
    1838
    时间
    2000ms
    内存
    128MiB
    难度
    普及+
    标签
    递交数
    0
    已通过
    0
    上传者