1 条题解

  • 0
    @ 2025-10-26 23:29:01

    火星巡演最大好评问题题解

    一、题目核心拆解

    本题是树结构路径查询 + 异或运算优化的综合问题,需分4步解决:

    1. 路径定位:找到城市 uuvv 的路径(树结构,依赖LCA分解路径);
    2. 最大异或节点筛选:在路径中找到使 sxis \oplus x_i 最大的城市 kk(需高效路径异或最大值查询);
    3. 异或和计算:计算路径中除 kk 外所有城市 xix_i 的异或和 SS(利用异或前缀和性质);
    4. 区间最大异或:在 W[0,w]W \in [0, w] 中找到使 WSW \oplus S 最大的值(经典区间异或最大值问题)。

    关键约束:n,m105n, m \leq 10^5,需所有步骤均为 O(logn)O(\log n)O(logM)O(\log M) 复杂度(MMxix_i 最大值,约 10910^9,对应30位二进制)。

    二、核心算法与预处理

    1. 树结构基础:LCA与异或前缀和

    (1)LCA(最近公共祖先)

    • 作用:将 uuvv 的路径分解为 uLCA(u,v)vu \to \text{LCA}(u,v) \to v,避免暴力遍历路径超时。
    • 实现:倍增法,预处理时间 O(nlogn)O(n\log n),单次查询 O(logn)O(\log n)
      • 定义 up[k][u]up[k][u]:节点 uu2k2^k 级祖先;
      • 定义 depth[u]depth[u]:节点 uu 到根的深度。

    (2)异或前缀和

    • 定义xor_sum[u]xor\_sum[u] 表示根节点到 uu 的路径上所有 xix_i 的异或和。
    • 路径异或和公式uuvv 的路径总异或和为
      $\text{total\_xor} = xor\_sum[u] \oplus xor\_sum[v] \oplus x_i[\text{LCA}(u,v)]$
      (原因:xor_sum[u]xor\_sum[u]uu \to \text{根}xor_sum[v]xor\_sum[v]vv \to \text{根}LCA\text{LCA} 被异或两次抵消,需补回一次)。

    2. 路径最大异或查询:可持久化字典树

    (1)问题需求

    需快速查询 uuvv 路径上,与给定 ss 异或值最大的 xix_i(即 max(sxi)\max(s \oplus x_i))。

    (2)可持久化字典树原理

    • 核心思想:每个节点 uu 对应一棵字典树,存储从根到 uu 路径上所有 xix_i 的30位二进制(高位到低位)。
    • 继承关系:节点 uu 的字典树基于其父节点的字典树构建,仅新增 xux_u 的二进制路径,保证空间效率 O(nlogM)O(n\log M)

    (3)查询逻辑

    uuvv 的路径可拆分为 uu \to \text{根}vv \to \text{根}LCA\text{LCA} \to \text{根},查询时:

    1. uu 的字典树、vv 的字典树、LCA\text{LCA} 父节点的字典树,合并得到路径上所有 xix_i 的字典树;
    2. 按二进制高位到低位遍历字典树,优先选择与 ss 当前位不同的分支,最大化 sxis \oplus x_i,同时记录对应 xix_i(记为 xkx_kkk 为目标节点)。

    3. 区间最大异或:带约束的01字典树

    (1)问题需求

    给定 SS 和区间 [0,w][0, w],求 max(WS)\max(W \oplus S)W[0,w]W \in [0, w])。

    (2)算法设计

    无需插入 [0,w][0, w] 所有数,而是在查询时结合 ww 的二进制约束,贪心选择最优位:

    • 从最高位(30位)到最低位遍历;
    • 对当前位 bbSS 的当前位),优先尝试选择 1b1-b(最大化异或),若选择后剩余位组成的数超过 ww 的对应部分,则只能选择 bb
    • 最终得到的路径对应最大的 WSW \oplus S

    三、完整解题流程

    步骤1:预处理

    1. 建树与LCA倍增预处理
      • 输入树的边,构建邻接表;
      • 预处理 upup 表(kk 取0到20,因 220>1052^{20} > 10^5)和 depthdepth 数组。
    2. 计算异或前缀和 xor_sumxor\_sum
      • 从根节点出发BFS/DFS,计算每个节点的 xor_sum[u]=xor_sum[parent[u]]xi[u]xor\_sum[u] = xor\_sum[parent[u]] \oplus x_i[u]
    3. 构建可持久化字典树
      • 从根节点出发,每个节点 uu 的字典树继承父节点,插入 xi[u]x_i[u] 的30位二进制。

    步骤2:处理单次查询(u,v,su, v, s

    1. 求LCA:计算 lca=LCA(u,v)\text{lca} = \text{LCA}(u, v)
    2. 查最大异或节点:用可持久化字典树查询 uuvv 路径上 max(sxi)\max(s \oplus x_i),得到对应 xkx_kkk 为跳过的节点)。
    3. 计算 SS
      $\text{total\_xor} = xor\_sum[u] \oplus xor\_sum[v] \oplus x_i[lca]$
      S=total_xorxkS = \text{total\_xor} \oplus x_k(异或中去掉 xkx_k 等价于再异或一次 xkx_k)。
    4. 求最大 WSW \oplus S:用带约束的01字典树,查询 [0,w][0, w] 中与 SS 异或最大的值,即为答案。

    四、关键代码实现(C++)

    1. LCA倍增预处理

    const int MAXN = 1e5 + 10;
    const int LOG = 20;
    vector<int> adj[MAXN];
    int up[LOG][MAXN], depth[MAXN];
    long long xor_sum[MAXN], x[MAXN]; // x[i]是城市i的x_i
    
    void dfs_lca(int u, int parent_u) {
        up[0][u] = parent_u;
        depth[u] = depth[parent_u] + 1;
        xor_sum[u] = xor_sum[parent_u] ^ x[u];
        for (int v : adj[u]) {
            if (v != parent_u) {
                dfs_lca(v, u);
            }
        }
    }
    
    void init_lca(int root, int n) {
        memset(up, 0, sizeof(up));
        depth[0] = 0;
        xor_sum[0] = 0;
        dfs_lca(root, 0);
        for (int k = 1; k < LOG; k++) {
            for (int u = 1; u <= n; u++) {
                up[k][u] = up[k-1][up[k-1][u]];
            }
        }
    }
    
    int lca_query(int u, int v) {
        if (depth[u] < depth[v]) swap(u, v);
        // 提升u到与v同深度
        for (int k = LOG-1; k >= 0; k--) {
            if (depth[u] - (1 << k) >= depth[v]) {
                u = up[k][u];
            }
        }
        if (u == v) return u;
        // 共同提升到LCA
        for (int k = LOG-1; k >= 0; k--) {
            if (up[k][u] != up[k][v]) {
                u = up[k][u];
                v = up[k][v];
            }
        }
        return up[0][u];
    }
    

    2. 可持久化字典树

    const int MAX_NODE = 1e5 * 32; // 1e5节点,每个32位
    int trie[MAX_NODE][2], cnt[MAX_NODE], version[MAXN];
    int tot = 0;
    
    void insert(int &root, int pre_root, long long x) {
        root = ++tot;
        int cur = root;
        pre_root = version[pre_root]; // 父节点的版本
        for (int i = 30; i >= 0; i--) {
            int bit = (x >> i) & 1;
            // 继承父节点的另一分支
            trie[cur][1 - bit] = trie[pre_root][1 - bit];
            // 新建当前分支
            trie[cur][bit] = ++tot;
            // 移动指针
            cur = trie[cur][bit];
            pre_root = trie[pre_root][bit];
        }
    }
    
    // 查询u到v路径上s^x_i的最大值,返回对应的x_i
    long long query_max_xor(int u, int v, int lca, long long s) {
        int root_u = version[u], root_v = version[v], root_lca = version[up[0][lca]];
        long long res = 0;
        for (int i = 30; i >= 0; i--) {
            int bit = (s >> i) & 1;
            // 优先选1-bit分支(最大化异或),需确保该分支在路径中存在
            if (trie[root_u][1 - bit] || trie[root_v][1 - bit] || trie[root_lca][1 - bit]) {
                res |= (1LL << i);
                bit = 1 - bit;
            }
            // 移动到下一位
            root_u = trie[root_u][bit];
            root_v = trie[root_v][bit];
            root_lca = trie[root_lca][bit];
        }
        return res ^ s; // 因res = s^x_k,故x_k = res ^ s
    }
    
    // 初始化可持久化字典树(root为1)
    void init_persistent_trie(int root, int n) {
        tot = 0;
        memset(trie, 0, sizeof(trie));
        version[0] = 0;
        // DFS构建,parent[u]为u的父节点
        function<void(int, int)> dfs = [&](int u, int parent_u) {
            insert(version[u], parent_u, x[u]);
            for (int v : adj[u]) {
                if (v != parent_u) {
                    dfs(v, u);
                }
            }
        };
        dfs(root, 0);
    }
    

    3. 带约束的区间最大异或

    long long max_xor_with_limit(long long S, long long w) {
        long long res = 0, current = 0;
        for (int i = 30; i >= 0; i--) {
            int bit_S = (S >> i) & 1;
            int max_bit = (w >> i) & 1;
            // 尝试选1-bit_S(最大化异或)
            int try_bit = 1 - bit_S;
            if (try_bit <= max_bit) {
                // 若选try_bit,剩余位可自由选(0~2^i-1),直接更新res
                res |= (1LL << i);
                if (try_bit < max_bit) {
                    // 剩余位可全选1,直接返回
                    res |= ((1LL << i) - 1);
                    break;
                }
                current |= (try_bit << i);
            } else {
                // 只能选bit_S,current累加
                current |= (bit_S << i);
            }
        }
        return res;
    }
    

    4. 主函数逻辑

    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0);
    
        int n, m;
        long long w;
        cin >> n >> m >> w;
    
        // 建图
        for (int i = 0; i < n-1; i++) {
            int u, v;
            cin >> u >> v;
            adj[u].push_back(v);
            adj[v].push_back(u);
        }
    
        // 输入x_i(1-based)
        for (int i = 1; i <= n; i++) {
            cin >> x[i];
        }
    
        // 预处理LCA和异或前缀和(根设为1)
        init_lca(1, n);
        // 预处理可持久化字典树
        init_persistent_trie(1, n);
    
        // 处理m次查询
        while (m--) {
            int u, v;
            long long s;
            cin >> u >> v >> s;
    
            // 步骤1:求LCA
            int l = lca_query(u, v);
    
            // 步骤2:查路径上s^x_i最大的x_k
            long long x_k = query_max_xor(u, v, l, s);
    
            // 步骤3:计算S
            long long total_xor = xor_sum[u] ^ xor_sum[v] ^ x[l];
            long long S = total_xor ^ x_k;
    
            // 步骤4:求[0,w]中W^S的最大值
            long long ans = max_xor_with_limit(S, w);
            cout << ans << '\n';
        }
    
        return 0;
    }
    

    五、关键细节与复杂度分析

    1. 细节说明

    • 可持久化字典树的版本管理version[u] 存储节点 uu 对应的字典树根,确保路径查询时能正确合并父节点信息。
    • 异或和计算的正确性:路径总异或和需补回 LCA\text{LCA}xix_i,避免重复抵消;去掉 xkx_k 需再异或一次 xkx_k,利用异或“自反性”(aa=0a \oplus a = 0)。
    • 区间约束的贪心逻辑:在 max_xor_with_limit 中,若选择的位小于 ww 对应位,剩余位可全选1,直接跳出循环,提升效率。

    2. 复杂度分析

    • 预处理:LCA O(nlogn)O(n\log n),可持久化字典树 O(nlogM)O(n\log M)
    • 单次查询:LCA O(logn)O(\log n),路径最大异或 O(logM)O(\log M),区间最大异或 O(logM)O(\log M)
    • 总复杂度O(nlogn+nlogM+mlogn+mlogM)O(n\log n + n\log M + m\log n + m\log M),完全适配 n,m105n, m \leq 10^5 的数据规模。
    • 1

    信息

    ID
    4224
    时间
    1000ms
    内存
    256MiB
    难度
    10
    标签
    递交数
    1
    已通过
    1
    上传者