ZZJC 2024 autumn Individual Contest - 11(题解)

预测难度

Easy:A、C
Medium:E、B、F
Hard:D

实际难度

Easy:A、C
Medium:F、B
Hard:D、E

A - Debug

分析

错误存在第7行
s.length()s.size()返回的类型为unsigned long long,当有符号型整数遇到无符号整数时类型自动转换为无符号,因此i >= s.length() - k永远无法成立,导致程序陷入死循环。
此处应改为i >= (int)s.length() - k

代码实现

#include <bits/stdc++.h>

#ifdef LOCAL
#include "algo/debug.hpp"
#else
#define debug(...) 42
#endif

int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
    std::cout << "Yes" << "\n";
    std::cout << 7 << '\n';
}

C - 两数之和

分析

由于\(n \leq 2 * 10^5\),如果进行双重循环遍历时间复杂为\(O(n^2)\),显然会超时。
考虑优化,使用std::muiltiset进行存储和查询,查询和存取的时间复杂度为\(O(logn)\),对于每个数\(y\)进行遍历,如果std::muiltiset存在\(x - y\)即输出YES,遍历结束后不存在即输出NO
同理也可以使用二分查找判断\(x - y\)是否存在。
最终的复杂度均为\(O(nlogn)\)
注意判断存在重复的情况。

代码实现(multiset版)

#include <bits/stdc++.h>

#ifdef LOCAL
#include "algo/debug.hpp"
#else
#define debug(...) 42
#endif

int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
    int n, x;
    std::cin >> n >> x;
    std::vector<int> a(n);
    std::multiset<int> st;
    for (int i = 0; i < n; ++i) {
        std::cin >> a[i];
        st.insert(a[i]);
    }
    for (int i = 0; i < n; ++i) {
        if (a[i] * 2 != x) {
            if (st.contains(x - a[i])) {
                std::cout << "YES" << '\n';
                return 0;
            }
        } else if (st.count(a[i]) > 1) {
            std::cout << "YES" << '\n';
            return 0;
        }
    }
    std::cout << "NO" << '\n';
}

代码实现(二分版)

#include <bits/stdc++.h>

#ifdef LOCAL
#include "algo/debug.hpp"
#else
#define debug(...) 42
#endif

int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
    int n, x;
    std::cin >> n >> x;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++i) {
        std::cin >> a[i];
    }
    std::sort(a.begin(), a.end());
    for (int i = 0; i < n; ++i) {
        int pos = std::ranges::lower_bound(a, x - a[i]) - a.begin();
        if (pos != n && a[pos] + a[i] == x && pos != i) {
            std::cout << "YES" << '\n';
            return 0;   
        }
    }
    std::cout << "NO" << '\n';
}

E - 排版

老东西看了这题应该很亲切吧,没错他又来了~

分析

本题主要难度在于如何处理字符串,对于若干行可以选择使用while(std::getline(std::cin, line))进行行读入。
对于空格的处理可以使用std::stringstream进行重载输入流,将读到的不含空格的字符串存入std::vector<std::string>数组中,最后按照题意处理即可。

代码实现

#include <bits/stdc++.h>

#ifdef LOCAL
#include "algo/debug.hpp"
#else
#define debug(...) 42
#endif

int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
    std::string line;
    while (std::getline(std::cin, line)) {
        std::stringstream is(line);
        std::vector<std::string> v;
        std::string p;
        while (is >> p) {
            std::transform(p.begin(), p.end(), p.begin(), tolower);
            v.push_back(p);
        }
        for (int i = 0; i < size(v); ++i) {
            if (i == 0 || v[i - 1].back() == '.' || v[i] == "i," || v[i] == "i." || v[i] == "i") {
                v[i][0] -= 32;
            }
            std::cout << v[i] << " \n"[i == size(v) - 1];
        }
    }
}

B - Tree Degree Optimization

分析

贪心。在确保每个点都有入度d(即最终的图为一棵树而非森林)的情况下,每次添加\((d^2-(d-1)^2)*a_i\)即可,一棵数的总度数为\(2 * n - 2\)

代码实现

#include <bits/stdc++.h>

#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif

int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
    #define int long long
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++i) {
        std::cin >> a[i];
    }
    int ans = 0;
    std::priority_queue<std::pair<int, int>, std::vector<std::pair<int, int>>, std::greater<>> q;
    for (int i = 0; i < n; ++i) {
        ans += a[i];
        q.emplace(3LL * a[i], 2);
    }
    for (int i = 1; i < n - 1; ++i) {
        auto [w, num] = q.top();
        q.pop();
        ans += w;
        q.emplace(w / (2 * num - 1) * (2 * num + 1), num + 1);
    }
    std::cout << ans << '\n';
}

F - Partition

分析

贪心,对k的大小进行分类讨论即可。
首先将所有的0取出放在最前面,随后进行分类讨论:
\(k > 0\)时可以发现一定有解,将所有的负数从小到大插入,正数从大到小插入即可;
\(k = 0\)时可以发现当\(\sum_{i = 1}^{i = n}a_i \ge 0\)时有解,此时将所有的正数从大到小插入,再将负数从小到大插入即可;
\(k < 0\)时可以发现当\(\sum_{i = 1}^{i = n}a_i \ge k\)时有解,此时将所有的正数从大到小插入,再将负数从小到大插入即可。

代码实现

#include <bits/stdc++.h>
#ifdef LOCAL
#include "algo/debug.h"
#else
#define debug(...) 42
#endif
int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
    #define int long long
    int n, k;
    std::cin >> n >> k;
    std::vector<int> a, b, c;
    for (int i = 0; i < n; ++i) {
        int x;
        std::cin >> x;
        if (x == 0) {
            c.push_back(x);
        } else if (x > 0) {
            a.push_back(x);
        } else {
            b.push_back(x);
        }
    }
    std::sort(a.rbegin(), a.rend()); // +
    std::sort(b.begin(), b.end()); // -
    int s = std::accumulate(a.begin(), a.end(), 0LL) + std::accumulate(b.begin(), b.end(), 0LL);
    if (k == 0) {
        if (s >= 0) {
            c.insert(c.end(), a.begin(), a.end());
            c.insert(c.end(), b.begin(), b.end());
            std::cout << "Yes" << "\n";
            for (int i = 0; i < n; ++i) {
                std::cout << c[i] << " \n"[i == n - 1];
            }
        } else {
            std::cout << "No" << '\n';
        }
    } else if (k < 0) {
        if (s >= k) {
            c.insert(c.end(), a.begin(), a.end());
            c.insert(c.end(), b.begin(), b.end());
            std::cout << "Yes" << "\n";
            for (int i = 0; i < n; ++i) {
                std::cout << c[i] << " \n"[i == n - 1];
            }
        } else {
            std::cout << "No" << "\n";
        }
    } else {
        c.insert(c.end(), b.begin(), b.end());
        c.insert(c.end(), a.begin(), a.end());
        std::cout << "Yes" << "\n";
        for (int i = 0; i < n; ++i) {
            std::cout << c[i] << " \n"[i == n - 1];
        }
    }
}

D - Conquer a New Region

分析

由于要让所有点到这个点的边权最小值之和最大,考虑把边按从大到小插入。
此时每次并入的边权值都是当前已并入边中最小的,那么只要判断并入哪个集合更优。
最后用并查集维护即可。

代码实现

#include <bits/stdc++.h>

#ifdef LOCAL
#include "algo/debug.hpp"
#else
#define debug(...) 42
#endif

#define int long long

struct DSU {
    std::vector<int> p, siz, d;
    DSU(int n) { init(n); }
    void init(int n) {
        p.resize(n);
        std::iota(p.begin(), p.end(), 0);
        siz.assign(n, 1);
        d.assign(n, 0);
    }
    int leader(int x) {
        while (x != p[x]) x = p[x] = p[p[x]];
        return x;
    }
    bool same(int x, int y) { return leader(x) == leader(y); }
    bool merge(int x, int y, int z) {
        x = leader(x), y = leader(y);
        if (x == y) return false;
        int dx = d[x] + siz[y] * z;
        int dy = d[y] + siz[x] * z;
        if (dx > dy) {
            d[x] = dx;
            siz[x] += siz[y], p[y] = x;
        } else {
            d[y] = dy;
            siz[y] += siz[x], p[x] = y;
        }
        return true;
    }
    int size(int x) { return siz[leader(x)]; }
    int dist(int x) { return d[leader(x)]; }
};

main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
    int n;
    while (std::cin >> n) {
        std::vector<std::array<int, 3>> edges(n - 1);
        for (auto &[c, a, b] : edges) {
            std::cin >> a >> b >> c;
            a--, b--;
        }
        std::sort(edges.rbegin(), edges.rend());
        DSU dsu(n);
        for (auto [c, a, b] : edges) {
            dsu.merge(a, b, c);
        }
        std::cout << dsu.dist(0) << '\n';
    }
}
posted @ 2024-10-28 14:06  sleeeeeping  阅读(43)  评论(0编辑  收藏  举报