[题解] CF1790E - XOR Tree

CF1790E - XOR Tree

题意

给定一颗无根树,在可以改变任意一个点的点权操作基础上,让树上任意简单路径的异或和不为 \(0\) ,问最少需要多少次操作。

思路

假设某个点为根,设 \(pre_x\)\(x\) 点到根的树上前缀异或和, \(a_x\)\(x\) 的点权,则 \(x\)\(y\) 之间简单路径的异或和即为 \(pre_x \oplus pre_y \oplus a_{lca_{x,y}}\) 。那么,自然题目可以转化为 \(pre_x \oplus pre_y \oplus a_{lca_{x,y}} \neq 0\)

假设出现违反题目的操作,那么我们可以将 \(a_{lca_{x,y}}\) 改成一个任意大的值,使得该点的子树前缀和无效(或者说加上了一个非常大的值,不会违反题目的情况)。

因此,可以发现,我们并不需要真的去修改点权,对其的修改相当于将以其为根的子树丢弃。那么,我们就可以设当前遍历到的节点为 \(x\) ,是否存在为其子树的节点 \(u\)\(v\) ,使得 \(pre_u \uplus a_x = pre_v\) 。对于这个过程,我们可以将前缀异或和存在 set 之中, dfs 儿子节点后,对两个集合,查看是否出现上述情况,如果出现该情况,那么说明这个点要被修改,信息待会要被掉了。枚举完条件后,需要合并一下信息,相当于凑齐子树信息。这样再一次 dfs 一遍即可。

要注意的一个点是,由于涉及到两个集合合并,因此可以使用启发式合并的方式合并集合。

代码

auto Main() -> void {
    int n;
    cin >> n;

    vector<int> a(n), pre(n);
    for (int i = 0; i < n; i += 1) {
        cin >> a[i];
        pre[i] = a[i];
    }

    vector adj(n, vector<int>{});
    for (int i = 0; i < n - 1; i += 1) {
        int u, v;
        cin >> u >> v;
        u -= 1;
        v -= 1;
        adj[u].emplace_back(v);
        adj[v].emplace_back(u);
    }

    auto dfs1 = [&](auto &self, int from, int come) -> void {
        for (auto to : adj[from]) {
            if (to != come) {
                pre[to] ^= pre[from];
                dfs(dfs, to, from);
            }
        }
    };
    dfs1(dfs1, 0, -1);

    vector<set<int>> st(n);
    int ans = 0;

    auto dfs2 = [&](auto &dfs, int from, int come) -> void {
        st[from].emplace(pre[from]);
        bool need = false;
        for (auto to : adj[from]) {
            if (to == come) {
                continue;
            }
            dfs(dfs, to, from);
            if (need) continue;
            if (st[from].size() < st[to].size()) {
                swap(st[from], st[to]);
            }
            for (auto x : st[to]) {
                if (st[from].contains(x ^ a[from])) {
                    need = true;
                    break;
                }
            }
            st[from].merge(st[to]);
        }
        ans += need;
        if (need) {
            st[from].clear();
        }
    };
    dfs2(dfs2, 0, -1);

    cout << ans << '\n';  
}
posted @ 2023-10-17 21:37  フランドール·スカーレット  阅读(37)  评论(0)    收藏  举报