abc 395 题解

ABC

D

解题思路

对于操作 2,我们可以将窝对换而企鹅不动。然后就是看实现了

CODE
void solve()
{
    int n = 0, q = 0;
    std::cin >> n >> q;
    // idx 第 i 个位置的窝的编号
    // nest 编号为 i 的窝在那个位置
    // p 第 i 只企鹅在的位置
    std::vector idx(n + 1, 0), nest(n + 1, 0), p(n + 1, 0);
    for (int i = 1; i <= n; i++) {
        idx[i] = nest[i] = p[i] = i;
    }

    while (q--) {
        int t, a, b;
        std::cin >> t;
        if (t == 1) {
            std::cin >> a >> b;
            p[a] = nest[b];
        }
        else if (t == 2) {
            std::cin >> a >> b;
            std::swap(idx[nest[a]], idx[nest[b]]);
            std::swap(nest[a], nest[b]);
        }
        else {
            std::cin >> a;
            std::cout << idx[p[a]] << '\n';
        }
    }
}

E

解题思路

在正图和矾土两张图里 Dijkstra。具体的,先正图的点 1 入队,常规松弛之后,额外对反图的点 1 进行松弛,即看做正图的点 1 和返图的点 1 之间有一条长度为 \(x\) 的边相连。注意松弛的时候是在正图里松弛还是反图里松弛就好了。

CODE
void solve()
{
    int n = 0, m = 0;
    i64 x = 0;
    std::cin >> n >> m >> x;
    for (int i = 0; i < m; i++) {
        int u = 0, v = 0;
        std::cin >> u >> v;
        g1[u].push_back(v);
        g2[v].push_back(u);
    }

    std::priority_queue<std::pair<i64, int>> q;
    std::vector dis(2 * n + 1, Inf);
    std::vector vis(2 * n + 1, false);
    dis[n + 1] = 0;
    q.push({ 0, 1 });

    auto upd = [&](int cur, i64 d, bool inv) -> void {
        for (auto to : (inv ? g2[cur] : g1[cur])) {
            if (inv) {
                to = -to;
            }
            if (not vis[n + to] && d + 1 < dis[n + to]) {
                dis[n + to] = d + 1;
                q.push({ -dis[n + to], to });
            }
        }
    };
    while (not q.empty()) {
        auto [d, cur] = q.top();
        q.pop();
        if (vis[n + cur]) {
            continue;
        }
        vis[n + cur] = true;

        bool inv = (cur < 0);
        upd(std::abs(cur), -d, cur < 0);
        if (x - d < dis[n - cur]) {
            dis[n - cur] = x - d;
            q.push({ d - x, -cur });
        }
    }
    std::cout << std::min(dis[0], dis[n + n]) << '\n';
}

F

解题思路

最主要要观察到这题的二分性质:假设 \(H = h\) 时有一个合法的构造方案,那么对于所有的 \(H \leq h\) 我们都可以找到合法构造方案,即从 \(H = h\) 的构造方案开始,对于每一对 \(U[i]\)\(D[i]\) 优先减 \(U[i]\),这样可以保证相邻两个数的差距只会越来越小。

知道可以二分 \(H\) 后我们就要开始考虑如何 check 了:假设我们已经维护出了 \(U[i - 1]\) 的范围 \([L, R]\),那么对于 \(U[i]\),首先为了满足条件 2,\(U[i]\) 应该在 \([L - x, R + x]\) 中,为了满足条件 1,\(U[i]\) 又应该在 \([max(0, mid - D[i]), min(mid, U[i])]\) 中,于是取两个区间的交集,若为空集则说明该 \(mid\) 不行。对于第 1 个位置,就令区间为 \([0, Inf]\),表示没有条件 2 的影响。

CODE
void solve()
{
    int n = 0, x = 0;
    std::cin >> n >> x;
    std::vector U(n, 0ll), D(n, 0ll);
    for (int i = 0; i < n; i++) {
        std::cin >> U[i] >> D[i];
    }

    i64 l = 1, r = Inf;
    for (int i = 0; i < n; i++) {
        r = std::min(r, U[i] + D[i]);
    }
    while (l <= r) {
        i64 m = l + r >> 1; 
        
        i64 L = 0, R = Inf;
        for (int i = 0; i < n; i++) {
            L = std::max(L - x, std::max(0ll, m - D[i]));
            R = std::min(R + x, std::min(m, U[i]));
            if (L > R) { 
                break;
            }
        }

        if (L <= R) {
            l = m + 1;
        }
        else {
            r = m - 1;
        }
    }
    
    i64 ans = 0;
    for (int i = 0; i < n; i++) {
        ans += U[i] + D[i] - r;
    }
    std::cout << ans << '\n';
    return;
}
posted @ 2025-03-03 17:09  Young_Cloud  阅读(19)  评论(0)    收藏  举报