(枚举 + 树上倍增)Codeforces Round 900 (Div. 3) G

Problem - G - Codeforces

题意:

思路:

首先,目标值和结点权值是直接联系的,最值不可能直接贪心,一定是考虑去枚举一些东西,依靠这种枚举可以遍历所有的有效情况,思考的方向一定是枚举

如果去直接在链上枚举的话, 复杂度是O(nq),肯定不行

注意到一条路径上的前缀或值不会超过 logV个,因此考虑枚举前缀或值

关于每次跳使前缀或值变化的最深的点,我是这样理解的

如果考虑在链上枚举,如果前缀或值不变,那么这样的枚举是无效的,我们直接考虑跳着枚举,只枚举所有有效情况

关于怎么跳其实可以参考树上倍增往上跳的跳法,记录一个数组指向下一个结点,在dfs上维护即可,有点像在树链上DP

Code:

cpp 复制代码
#include <bits/stdc++.h>

#define int long long

constexpr int N = 2e5 + 10;

std::vector<int> adj[N];

int n;
int a[N];
int dep[N];
int f[N][33], s[N][33], lst[N][33];

void dfs(int u, int fa) {
    dep[u] = dep[fa] + 1;
    f[u][0] = fa;
    for (int j = 1; j <= 30; j ++) f[u][j] = f[f[u][j - 1]][j - 1];
    int val = a[u];
    for (int j = 30; j >= 0; j --) {
        if (!((val >> j) & 1)) {
            lst[u][j] = lst[fa][j];
            s[u][j] = s[fa][j];
        }else {
            lst[u][j] = u;
            s[u][j] = s[fa][j] + 1;
        }
    }
    for (auto v : adj[u]) {
        if (v == fa) continue;
        dfs(v, u);
    }
}
int lca(int u, int v) {
    if (dep[u] < dep[v]) std::swap(u, v);
    for (int j = 30; j >= 0; j --) {
        if (dep[f[u][j]] >= dep[v]) {
            u = f[u][j];
        }
    }
    if (u == v) return u;
    for (int j = 30; j >= 0; j --) {
        if (f[u][j] != f[v][j]) {
            u = f[u][j];
            v = f[v][j];
        }
    }
    return f[u][0];
}
int calc(int x, int y, int lca) {
    int res = 0;
    for (int j = 0; j <= 30; j ++) {
        if (s[x][j] + s[y][j] - s[lca][j] - s[f[lca][0]][j]) res ++;
    }
    return res;
}
void solve() {
    std::cin >> n;
    for (int i = 1; i <= n; i ++) {
        adj[i].clear();
        dep[i] = 0;
        for (int j = 30; j >= 0; j --) {
            f[i][j] = s[i][j] = lst[i][j] = 0;
        }
    }
    for (int i = 1; i <= n; i ++) std::cin >> a[i];
    for (int i = 1; i <= n - 1; i ++) {
        int u, v;
        std::cin >> u >> v;
        adj[u].push_back(v);
        adj[v].push_back(u);
    }
    dfs(1, 0);

    int q;
    int ans = 0;
    std::cin >> q;
    while(q --) {
        int x, y;
        std::cin >> x >> y;
        int cur = x, val = a[x];
        ans = 0;
        while(1) {
            int nxt = 0, mx = 0;
            ans = std::max(ans, calc(x, cur, lca(x, cur)) + calc(cur, y, lca(cur, y)));
            for (int j = 30; j >= 0; j --) {
                if (!((val >> j) & 1)) {
                    if (dep[lst[cur][j]] >= dep[lca(x, y)]) {
                        if (dep[lst[cur][j]] > mx) {
                            mx = dep[lst[cur][j]];
                            nxt = lst[cur][j];
                        }
                    }
                }
            }
            if (!mx) break;
            val |= a[nxt];
            cur = nxt;
        }
        cur = y, val = a[y];
        while(1) {
            int nxt = 0, mx = 0;
            ans = std::max(ans, calc(x, cur, lca(x, cur)) + calc(cur, y, lca(cur, y)));
            for (int j = 30; j >= 0; j --) {
                if (!((val >> j) & 1)) {
                    if (dep[lst[cur][j]] >= dep[lca(x, y)]) {
                        if (dep[lst[cur][j]] > mx) {
                            mx = dep[lst[cur][j]];
                            nxt = lst[cur][j];
                        }
                    }
                }
            }
            if (!mx) break;
            val |= a[nxt];
            cur = nxt;
        }
        std::cout << ans << " ";
    }
    std::cout << "\n";
}
signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(nullptr);

    int t = 1;
    std::cin >> t;
    while(t --) {
        solve();
    }
    return 0;
}
相关推荐
前端小L8 分钟前
图论专题(二十五):最小生成树(MST)——用最少的钱,连通整个世界「连接所有点的最小费用」
算法·矩阵·深度优先·图论·宽度优先
前端小L12 分钟前
图论专题(二十三):并查集的“数据清洗”——解决复杂的「账户合并」
数据结构·算法·安全·深度优先·图论
CoovallyAIHub27 分钟前
破局红外小目标检测:异常感知Anomaly-Aware YOLO以“俭”驭“繁”
深度学习·算法·计算机视觉
点云SLAM1 小时前
图论中邻接矩阵和邻接表详解
算法·图论·slam·邻接表·邻接矩阵·最大团·稠密图
啊董dong1 小时前
课后作业-2025年11月23号作业
数据结构·c++·算法·深度优先·noi
星释1 小时前
Rust 练习册 80:Grains与位运算
大数据·算法·rust
zzzsde2 小时前
【C++】C++11(1):右值引用和移动语义
开发语言·c++·算法
sheeta19985 小时前
LeetCode 每日一题笔记 日期:2025.11.24 题目:1018. 可被5整除的二进制前缀
笔记·算法·leetcode
gfdhy10 小时前
【c++】哈希算法深度解析:实现、核心作用与工业级应用
c语言·开发语言·c++·算法·密码学·哈希算法·哈希