ABC 448 个人题解

ABC448 个人题解

其实我觉得这一场比赛应该要有题目是一些有关 448 这个数的性质的……

A. chmin

题意

给你长度为 \(N\) 的数组 \(A\) 和一个整数 \(X\), 对于 \(i\)\(1\)\(N\), 判断 \(A_i\) 是否小于 \(X\), 小于输出 \(1\) 并把 \(X\) 赋为 \(A_i\), 否则输出 \(0\)

思路

直接模拟, 这里没有读完 \(A\) 数组, 时间/空间 \(O(n)/O(1)\)

代码

// A - chmin
// 2000 ms
// 1024 MB
// https://atcoder.jp/contests/abc448/tasks/abc448_a
// Made by Billlly 喵~

#include <bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using real = long double;

int tests = 1;

void solve() {
    int n, x;
    std::cin >> n >> x;
    for (int i = 1; i <= n; ++i) {
        int a;
        std::cin >> a;
        if (a < x) {
            x = a;
            std::cout << 1 << '\n';
        } else
            std::cout << 0 << '\n';
    }
}

int main() {
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    std::cout.tie(0);
    while (tests--) {
        solve();
    }
    return 0;
}

B. Pepper Addiction

题意

\(N\) 盘菜和 \(M\) 种胡椒

\(i\) 盘菜有两个属性 \(A_i\)\(B_i\), 代表这盘菜最多\(B_i \ g\) 胡椒 \(A_i\)

\(i\) 种胡椒有一个属性 \(C_i\), 表示最多使用 \(C_i \ g\) 该种胡椒

现在问你最多可以使用多少胡椒

思路

对于每盘菜, 因为最多\(B_i\), 所以我们就尽量放满, 后面没有了所需要的胡椒就不放了, 显然是正确的

时间/空间 \(O(n)/O(n + m)\)

我写的时候排序了, 实际上是 useless 的

代码

// B - Pepper Addiction
// 2000 ms
// 1024 MB
// https://atcoder.jp/contests/abc448/tasks/abc448_b
// Made by Billlly 喵~

#include <bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using real = long double;

int tests = 1;

void solve() {
    int n, m;
    std::cin >> n >> m;
    std::vector<int> c(m);
    for (int i = 0; i < m; ++i)
        std::cin >> c[i];

    std::vector<std::pair<int, int>> dish(n);
    for (int i = 0; i < n; ++i) {
        std::cin >> dish[i].first >> dish[i].second;
        --dish[i].first;
    }
    i64 ans = 0;
    for (int i = 0; i < n; ++i) {
        int tp = dish[i].first;
        int amt = dish[i].second;
        int use = std::min(c[tp], amt);
        ans += use;
        c[tp] -= use;
    }

    std::cout << ans << '\n';
}

int main() {
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    std::cout.tie(0);
    while (tests--) {
        solve();
    }
    return 0;
}

C. Except and Min

题意

给定长度为 \(N\) 的数组 \(A\), 另有 \(Q\) 次查询

每次查询给定 \(K\) 个数 \(B_1 ... B_K\), 然后问你如果在 \(A\) 中删除这若干个数(各一个), 最小值是多少

思路

注意到 \(K\) 只有 \(5\), 所以我们直接 multiset 做完了, 时间复杂度 \(O(QK log N)\)

代码

// C - Except and Min
// 2000 ms
// 1024 MB
// https://atcoder.jp/contests/abc448/tasks/abc448_c
// Made by Billlly 喵~

#include <bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using real = long double;

int tests = 1;

void solve() {
    int n, q;
    std::cin >> n >> q;
    std::vector<int> a(n + 1);
    std::multiset<int> st;
    for (int i = 1; i <= n; ++i) {
        std::cin >> a[i];
        st.emplace(a[i]);
    }
    while (q--) {
        int k;
        std::cin >> k;
        std::vector<int> tmp;
        for (int i = 1; i <= k; ++i) {
            int b;
            std::cin >> b;
            tmp.emplace_back(a[b]);
            st.erase(st.find(a[b]));
        }
        std::cout << *st.begin() << '\n';
        for (const auto& x : tmp)
            st.emplace(x);
    }
}

int main() {
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    std::cout.tie(0);
    while (tests--) {
        solve();
    }
    return 0;
}

D. Integer-duplicated Path

题意

给定一颗 \(N\) 个节点的点权树, 问你对于所有的 \(i\)\(1 \sim N\), 路径 \(1 - i\) 是否存在两个相同权值的节点

思路

DFS + 桶, 从节点 \(1\) 开始遍历整棵树, 时间复杂度 \(O(n)\), 细节见代码

代码

// D - Integer-duplicated Path
// 2000 ms
// 1024 MB
// https://atcoder.jp/contests/abc448/tasks/abc448_d
// Made by Billlly 喵~

#include <bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using real = long double;

int tests = 1;

void solve() {
    int n;
    std::cin >> n;
    std::vector<int> a(n + 1);
    for (int i = 1; i <= n; ++i)
        std::cin >> a[i];

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

    std::vector<std::string> ans(n + 1);
    std::unordered_map<int, int> cnt;
    int dup = 0;

    auto dfs = [&](auto&& dfs, int u, int p) -> void {
        cnt[a[u]] += 1;
        if (cnt[a[u]] == 2)
            dup += 1;
        ans[u] = (dup > 0) ? "Yes" : "No";

        for (const auto& v : adj[u]) {
            if (v == p)
                continue;
            dfs(dfs, v, u);
        }

        cnt[a[u]] -= 1;
        if (cnt[a[u]] == 1)
            dup -= 1;
    };

    dfs(dfs, 1, 0);

    for (int i = 1; i <= n; ++i)
        std::cout << ans[i] << '\n';
}

int main() {
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    std::cout.tie(0);
    while (tests--) {
        solve();
    }
    return 0;
}

E. Simple Division

题意

给定 \(M\) 和一个大数 \(N\) 的游程编码, 求 \(\lfloor \frac{N}{M} \rfloor \bmod 10007\)

游程编码是说给定 \(K\) 个二元组 \((c_1, l_1) \dots (c_K, l_K)\), 数 \(N\)\(l_1\)\(c_1\) 连接 \(l_2\)\(c_2\) 一直连接到 \(l_K\)\(c_K\)

思路

直接构造 \(N\) 显然不可行, 注意到 \(N\) 最大可能有 \(10^{14}\) 位, 无法直接表示

考虑模运算的性质, 设 \(x = N \bmod 10007\), \(y = N \bmod M\)
则有:

\[\lfloor \frac{N}{M} \rfloor = \frac{N - y}{M} \]

于是:

\[\lfloor \frac{N}{M} \rfloor \bmod 10007 = \frac{x - y}{M} \bmod 10007 \]

其中除法是模 \(10007\) 意义下的乘法逆元, 即乘以 \(M\) 在模 \(10007\) 下的逆元

现在问题转化为分别计算 \(x = N \bmod 10007\)\(y = N \bmod M\)

考虑从高位到低位构造 \(N\) 的模 \(P\) 值 (\(P = 10007\)\(P = M\))

  • 当前值 \(cur\), 新增 \(l\) 个数字 \(d\)
  • 新的值 \(cur' = cur \times 10^l + d \times (10^{l-1} + 10^{l-2} + \dots + 1) \pmod{P}\)

因此我们需要快速计算 \(10^l \bmod P\) 和等比数列和 \(S = \frac{10^l - 1}{9} \bmod P\)

由于 \(P\) 可能是任意数, 需要处理分母 \(9\) 的逆元存在性问题
但我们实际上可以直接计算 \(d \times S \bmod P\), 而 \(S\) 可以用递推公式计算:

\[S_0 = 0, \quad S_i = (S_{i-1} \times 10 + 1) \bmod P \]

这样 \(S_l\) 即为所求, 时间复杂度 \(O(l)\), 但是 \(l\) 可达 \(10^9\) 就爆炸了

于是不难想到用快速幂优化, 设 \(f(a, b) = (a^b, 1 + a + a^2 + \dots + a^{b-1}) \pmod{P}\)
则有递推关系:

  • \(b\) 为偶数: \(f(a, b) = f(a, b/2)^2\)
  • \(b\) 为奇数: \(f(a, b) = f(a, b-1) \times a\)

具体实现时用 pair 存储 \((a^b, 1+a+\dots+a^{b-1})\)

总时间复杂度 \(O(K \log \max(l_i))\), 满足限制

代码

// E - Simple Division
// 2000 ms
// 1024 MB
// https://atcoder.jp/contests/abc448/tasks/abc448_e
// Made by Billlly 喵~

#include <bits/stdc++.h>
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using real = long double;

int tests = 1;

void solve() {
    i64 K, M;
    std::cin >> K >> M;
    const i64 MOD = 10007;
    
    auto pm = i64 a, i64 b, i64 mod {
        i64 res = 1 % mod;
        while (b > 0) {
            if (b & 1) res = res * a % mod;
            a = a * a % mod;
            b >>= 1;
        }
        return res;
    };
    
    auto ps = i64 a, i64 n, i64 mod {
        if (n == 0) return std::pair{1 % mod, 0LL};
        std::pair<i64, i64> res = {1 % mod, 0};
        i64 base_a = a;
        i64 base_s = 1;
        while (n > 0) {
            if (n & 1) {
                res.first = res.first * base_a % mod;
                res.second = (res.second * base_a + base_s) % mod;
            }
            base_s = base_s * (1 + base_a) % mod;
            base_a = base_a * base_a % mod;
            n >>= 1;
        }
        return res;
    };
    
    i64 invM = pm(M, MOD - 2, MOD);
    
    i64 nm = 0, r = 0;
    for (i64 i = 0; i < K; ++i) {
        char c;
        i64 l;
        std::cin >> c >> l;
        i64 d = c - '0';
        
        auto t1 = ps(10, l, MOD);
        nm = (nm * t1.first + d * t1.second) % MOD;
        
        auto t2 = ps(10, l, M);
        r = (r * t2.first + d * t2.second) % M;
    }
    
    i64 ans = (nm - r) % MOD;
    if (ans < 0) ans += MOD;
    ans = ans * invM % MOD;
    std::cout << ans << '\n';
}

int main() {
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    std::cout.tie(0);

    while (tests--) {
        solve();
    }
    return 0;
}

F. Authentic Traveling Salesman Problem

题意

给定 \(N\) 个平面点 \((x_i, y_i)\), 找一条从 \(1\) 出发遍历每个其它点恰好一次的回路, 满足相邻两个遍历的节点的曼哈顿距离之和不超过 \(10 ^ {10}\)

思路

其实这很像莫队的优化, 具体可以看看这一道题目

然后就做完了

代码

// F - Authentic Traveling Salesman Problem
// 2000 ms
// 1024 MB
// https://atcoder.jp/contests/abc448/tasks/abc448_f
// Made by Billlly 喵~

#include <bits/stdc++.h>

using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
using real = long double;

int tests = 1;

void solve() {
    int n;
    std::cin >> n;

    struct pt {
        int x, y, id;
    };

    std::vector<pt> v(n);
    for (int i = 0; i < n; ++i) {
        std::cin >> v[i].x >> v[i].y;
        v[i].id = i + 1;
    }

    if (n == 1) {
        std::cout << 1 << '\n';
        return;
    }

    std::sort(v.begin(), v.end(), [](pt a, pt b) { return a.x < b.x; });

    static const int blk = std::max((int)std::sqrt(n), 1);

    for (int i = 0; i < n; i += blk) {
        int r = std::min(i + blk, n);
        int b = i / blk;

        auto cmp = [b](pt p, pt q) {
            if (b % 2 == 0) {
                return p.y < q.y;
            } else {
                return p.y > q.y;
            }
        };
        std::sort(v.begin() + i, v.begin() + r, cmp);
    }

    int bg = 0;
    for (int i = 0; i < n; ++i) {
        if (v[i].id == 1) {
            bg = i;
            break;
        }
    }

    for (int i = 0; i < n; ++i) {
        int pos = (bg + i) % n;
        std::cout << v[pos].id << " \n"[i == n - 1];
    }
}

int main() {
    std::ios::sync_with_stdio(0);
    std::cin.tie(0);
    std::cout.tie(0);
    while (tests--) {
        solve();
    }
    return 0;
}

G. Conquest

感觉是不可做的 AI 题, 润

posted @ 2026-03-07 21:56  Billlly  阅读(191)  评论(0)    收藏  举报