2025年 蓝桥杯C/C++B组省赛 个人代码

第十六届蓝桥杯C/C++B组省赛个人代码

A. 移动距离


  • 最后做的填空,凭感觉猜的。
  • 先往右走欧几里得距离,再往上转到目标点。
  • 证明需要画图,这里就不写了。
  • 不知道有反三角函数,求弧长用的实数域二分答案求角度占比。

B. 客流量上限


不会,第二个填空从来没做出来过。

C. 可分解的正整数


赛时看一眼填空不太好做,直奔C题。
没认真看题思考,只取了3的倍数(序列长度是3的),毫无疑问WA

解题思路

  • 对于一个正整数\(n\),可以写成\(\sum_{i=-n+1}^{n}i\)
  • \(n=1\)时,序列长度只为2。
  • 所以除了\(1\)都可以。

D. 产值调整


赛时直接猜,当操作次数非常多的时候,三个数会趋向于相等。
最大操作次数是\(log\)级别的,实在不会就打表,最多几十次就会相等。

AC代码

#include<bits/stdc++.h>

void Main() {
    int a, b, c, k;
    std::cin >> a >> b >> c >> k;
    while (k--) {
        int x = b + c >> 1;
        int y = a + c >> 1;
        int z = a + b >> 1;
        a = x, b = y, c = z;
        if (a == b && a == c) {
            break;
        }
    }
    std::cout << a << " " << b << " " << c << "\n";
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0), std::cout.tie(0);
    int T = 1;
    std::cin >> T;
    while (T--) {
        Main();
    }
    return 0;
}

E. 画展布置


解题思路

因为可以随便取\(m\)个,并打乱顺序,所以尽量取数值上较为集中的\(m\)个数。
先排个序,维护个前缀和,然后长度\(m\)的滑动窗口,取连续的\(m\)个即可。

  • 时间复杂度\(O(nlogn)\)

AC代码

#include<bits/stdc++.h>

using i64 = long long;

void Main() {
    int n, m;
    std::cin >> n >> m;
    std::vector<i64> a(n + 1);
    for (int i = 1; i <= n; i++) {
        std::cin >> a[i];
        a[i] *= a[i];
    }
    sort(a.begin() + 1, a.end());
    std::vector<i64> pre(n + 1);
    for (int i = 1; i <= n; i++) {
        pre[i] = pre[i - 1] + a[i] - a[i - 1];
    }
    i64 ans = 1e18;
    for (int i = m; i <= n; i++) {
        ans = std::min(ans, pre[i] - pre[i - m + 1]);
    }
    std::cout << ans << "\n";
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0), std::cout.tie(0);
    int T = 1;
    // std::cin >> T;
    while (T--) {
        Main();
    }
    return 0;
}

F. 水质检测


赛时贪心漏情况了,没考虑全面,WA一半。

解题思路

  • 最初思路肯定是把所有联通块挑出来,相邻的联通块连接。
  • 为了方便,把竖着两个字符变成了二进制数。'#'表示\(1\),'.'表示\(0\)
  • 时间复杂度\(O(n)\)

赛时错误代码

#include<bits/stdc++.h>

void Main() {
    std::string s[2];
    std::cin >> s[0] >> s[1];
    std::vector<std::pair<int, int>> piece;
    for (int i = 0; i < s[0].size(); i++) {
        int t = 0;
        for (int j = 0; j < 2; j++) {
            if (s[j][i] == '#') {
                t += 1 << (1 - j);
            }
        }
        if (t) {
            piece.push_back({i, t});
        }
    }
    int ans = 0;
    for (int i = 1; i < piece.size(); i++) {
        auto &[l, v1] = piece[i - 1];
        auto &[r, v2] = piece[i];
        if (r - l == 1 && v1 & v2) {
            continue;
        }
        if (v1 == 3 || v2 == 3 || v1 == v2) {
            ans += r - l - 1;
        } else {
            ans += r - l;
            // v2 = 3;
        }
    }
    std::cout << ans << "\n";
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0), std::cout.tie(0);
    int T = 1;
    // std::cin >> T;
    while (T--) {
        Main();
    }
    return 0;
}

AC代码

#include<bits/stdc++.h>

void Main() {
    std::string s[2];
    std::cin >> s[0] >> s[1];
    std::vector<std::pair<int, int>> piece;
    for (int i = 0; i < s[0].size(); i++) {
        int t = 0;
        for (int j = 0; j < 2; j++) {
            if (s[j][i] == '#') {
                t += 1 << (1 - j);
            }
        }
        if (t) {
            piece.push_back({i, t});
        }
    }
    int ans = 0;
    for (int i = 1; i < piece.size(); i++) {
        auto &[l, v1] = piece[i - 1];
        auto &[r, v2] = piece[i];
        if (r - l == 1 && v1 & v2) {
            continue;
        }
        if (v1 == 3 || v2 == 3 || v1 == v2) {
            ans += r - l - 1;
        } else {
            ans += r - l;
            v2 = 3;
        }
    }
    std::cout << ans << "\n";
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0), std::cout.tie(0);
    int T = 1;
    // std::cin >> T;
    while (T--) {
        Main();
    }
    return 0;
}

区别就是else里是否对\(v2\)进行赋值\(3\)
对于两个联通块连接处一上一下(一个\(1\),一个\(2\))的情况,比如\(21\),可以变成\(23\)或者\(31\)
但是对于连着三个连通块上下上或者下上下的情况,比如\(212\),变成\(232\)是最优的,而变成\(312\),则再需要一个变成\(332\)才能联通三个。
所以当上述所述情况时,尽量选择让后者变成\(3\)(两个'#'),这样就有可能在联通第三个联通块时少用一个。

G. 生产车间


赛时没做数据,过样例直接交了,估计WA一半。

解题思路

  • 树上背包
  • 从最底层往上,用\(u\)节点的子节点对\(u\)进行背包。
  • dp[i][j]表示节点\(i\)能否获得\(j\)的价值。
  • 为什么要从下往上,因为要使用子节点对父节点更新信息时,你要保证子节点的信息已经全部更新完了。
  • 整体过程直接DFS,回溯后就可以保证已经访问并更新了所有子节点,再对当前节点dp。我赛时还记录了深度,按层来,完全按照由下到上,有点蠢了。
  • 时间复杂度感觉是\(O(n^3)\),不知道为啥能跑这么快。
  • 好像可以\(bitset\)优化,也有人用FFT。(暂时不会)

赛时错误代码+错误指出(就差一点,没考虑到根节点也有可能度是1,被迫当成叶子了)

#include<bits/stdc++.h>
using namespace std;

#define int long long

using i64 = long long;
using i128 = __int128;
using u64 = unsigned long long;

int n;
const int N = 1e3 + 2;
int d[N], fa[N], dp[N][N], a[N];
vector<vector<int>> e(N), D(N);

void dfs(int x, int f) {
    fa[x] = f;
    d[x] = d[f] + 1;
    for (auto u : e[x]) {
        if (u != f) {
            dfs(u, x);
        }
    }
}

void Main() {
    cin >> n;
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    for (int i = 1; i < n; i++) {
        int u, v;
        cin >> u >> v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    dfs(1, 0);
    for (int i = 1; i <= n; i++) { // 这里不该有1,从2开始,单独D[1].push_back(1)就AC了,哭死
        if (e[i].size() == 1) {
            dp[i][a[i]] = a[i];
        }
        D[d[i]].push_back(i);
    }
    int len = n;
    while (1) {
        set<int> st;
        while (len > 0 && !D[len].size()) {
            len--;
        }
        if (len == 0) {
            break;
        }
        for (auto i : D[len]) {
            st.insert(fa[i]);
        }
        for (auto i : st) {
            for (auto j : e[i]) {
                if (j != fa[i]) {
                    for (int k = a[i]; k >= 0; k--) {
                        for (int kk = a[j]; kk >= 0; kk--) {
                            if (k >= kk) {
                                dp[i][k] = max(dp[i][k], dp[i][k - kk] + dp[j][kk]);
                            }
                        }
                    }
                }
            }
        }
        len--;
    }
    int ans = 0;
    for (int i = 0; i <= a[1]; i++) {
        ans = max(ans, dp[1][i]);
    }
    cout << ans << "\n";
}

signed main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0), std::cout.tie(0);
    int T = 1;
    // std::cin >> T;
    while (T--) {
        Main();
    }
    return 0;
}

AC代码

#include<bits/stdc++.h>

const int N = 1e3 + 5;

int n;
int dp[N][N], a[N];
std::vector<int> e[N];

void dfs(int x, int fa) {
    if (e[x].size() == 1 && x != 1) {
        dp[x][0] = 1;
        dp[x][a[x]] = 1;
        return;
    }
    for (auto u : e[x]) {
        if (u != fa) {
            dfs(u, x);
        }
    }
    dp[x][0] = 1;
    for (auto i : e[x]) {
        if (i != fa) {
            for (int j = a[x]; j >= 0; j--) {
                for (int k = 0; k <= a[i] && j + k <= a[x]; k++) {
                    dp[x][j + k] |= dp[x][j] & dp[i][k];
                }
            }
        }
    }
}

void Main() {
    std::cin >> n;
    for (int i = 1; i <= n; i++) {
        std::cin >> a[i];
    }
    for (int i = 1; i < n; i++) {
        int u, v;
        std::cin >> u >> v;
        e[u].push_back(v);
        e[v].push_back(u);
    }
    dfs(1, 0);
    for (int i = a[1]; i >= 0; i--) {
        if (dp[1][i]) {
            std::cout << i << "\n";
            return;
        }
    }
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0), std::cout.tie(0);
    int T = 1;
    // std::cin >> T;
    while (T--) {
        Main();
    }
    return 0;
}

H. 装修报价


第一眼感觉不太好做,赛时还搓了半天组合数板子,发现没啥用。
试了好几种思路,写了个暴力DFS当对拍,来验证想法对不对,试出来了。

解题思路

  • 一道不算太难的小规律题。
  • \(a_i\)能否对答案产生贡献,取决于它前面的符号。
  • \(a_i\)前面的符号是\(+\)时,可以将它变成\(-\),这样这两种方案的结果相加,就把这个数字给抵消了。
  • 一个特殊的点,\(a_1\)前面的符号只能是\(+\),所以\(a_1\)(包括和它相异或的异或值)一定会产生\(a_1 \times方案数\)的贡献。
  • 答案就是前\(i\)个数异或前缀和来产生贡献,下一个符号一定不是异或,后面的符号可以随便,三种其中一个,方案数\(2 \times3^k\)
  • 然后漏了一种,所有数异或在一块,额外加上就行。就是因为这个,调了半天。
  • 时间复杂度\(O(nlogn)\)

AC代码

#include<bits/stdc++.h>

using i64 = long long;

constexpr int mod = 1e9 + 7;

i64 power(i64 a, int b) {
    if (b < 0) {
        return 0;
    }
    i64 res = 1;
    for (; b; a = a * a % mod, b >>= 1) {
        if (b & 1) {
            res = res * a % mod;
        }
    }
    return res;
}

void Main() {
    int n;
    std::cin >> n;
    std::vector<int> a(n);
    for (int i = 0; i < n; i++) {
        std::cin >> a[i];
    }
    int pre = a[0];
    int ans = 0;
    for (int i = 0; i < n - 1; i++) {
        ans = (ans + pre * 2 % mod * power(3, n - i - 2) % mod) % mod;
        pre ^= a[i + 1];
    }
    ans = (ans + pre) % mod;
    std::cout << ans << "\n";
}

int main() {
    std::ios::sync_with_stdio(false);
    std::cin.tie(0), std::cout.tie(0);
    int T = 1;
    // std::cin >> T;
    while (T--) {
        Main();
    }
    return 0;
}

总结


总的来说,唐完了,远不如去年稳。
半小时写CDEF,四个题错两个,快速把简单题做错,然后去做麻烦的题,幸好最后一个AC了。
有的细节地方没处理好,树上背包差点AC,oi赛制自己造小数据测不出来,一判就WA。

posted @ 2025-04-14 23:57  sunjiànqiáng  阅读(234)  评论(0)    收藏  举报