CCPC 2025 福建邀请赛

M. 致谢

题目大意

已知第n届FJCPC承办学校英文缩写,给定n,输出学校英文缩写

解题思路

vec存学校缩写,按下标输出即可

代码实现

#include <bits/stdc++.h>

using i64 = long long;

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

    std::vector<std::string> ans = {"FZU", "FNU", "FZU", "FZU", "FAFU", "HQU", "MJU", "XMUT", "QNU", "JMU", "FZU"};

    int n;
    std::cin >> n;
    std::cout << ans[n - 1] << "\n";
}

G. 炒股高手

题目大意

给你n个交易日股票金额,你可以在那天买入或者卖出(可以操作非整数份的股票),再给你m个日期,问初始有k的资金,每个时间段结束后会有多少资金

解题思路

每次在谷的时候买入即可,前缀和预处理

代码实现

#include <bits/stdc++.h>

using i64 = long long;

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

    int n, m;
    std::cin >> n >> m;

    std::vector<i64> a(n), b(n), pre(n + 1);
    for (int i = 0; i < n; i++) {
        std::cin >> a[i];
        if (i && a[i] > a[i - 1]) {
            b[i] = a[i] - a[i - 1];
        }
        pre[i + 1] = pre[i] + b[i];
    }

    i64 k;
    std::cin >> k;

    while (m--) {
        int s, t;
        std::cin >> s >> t;
        std::cout << k + pre[t] - pre[s] << "\n";
    }
}

J. 构造大师CFJ

题目大意

给定一个数字n,每次可以加上n的一个因子,但是每次加上的因子要不同,用不超过100次操作使它变成一个完全平方数

解题思路

考虑都去往同一个完全平方数,发现去往一个指数为偶数的2的幂较为容易实现,每次加上lowbit(n)即可

代码实现

#include <bits/stdc++.h>

using i64 = long long;

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

    int tt;
    std::cin >> tt;

    while (tt--) {
        i64 n;
        std::cin >> n;

        std::vector<i64> ans;
        while (n < (1ll << 40)) {
            ans.push_back(n & -n);
            n += n & -n;
        }

        std::cout << ans.size() << "\n";
        if (ans.empty()) {
            std::cout << "\n";
        } else {
            for (auto x : ans) {
                std::cout << x << " \n"[x == ans.back()];
            }
        }
    }
}

K. VERTeX

题目大意

给你一棵树并告诉你所有的边权,边权为两点权之和,求任意一组满足条件的所有点权

解题思路

设根节点1的点权为x,可以先dfs处理出所有节点的值,偶数层的节点可以表示为w-x的形式,奇数层可以表示为w+x的形式,最后再根据奇偶层数来用范围来限制解的区间,如果区间不为空则有解,否则无解

代码实现

#include <bits/stdc++.h>

using i64 = long long;

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

    int n;
    std::cin >> n;

    std::vector<std::vector<std::array<int, 2>>> g(n + 1, std::vector<std::array<int, 2>>());
    for (int i = 1; i < n; i++) {
        int u, v, w;
        std::cin >> u >> v >> w;
        g[u].push_back({v, w});
        g[v].push_back({u, w});
    }

    std::vector<int> dp(n + 1), dep(n + 1);
    auto dfs = [&](auto &&self, int u, int fa) -> void {
        dep[u] = dep[fa] + 1;
        for (auto [v, w] : g[u]) {
            if (v != fa) {
                dp[v] = w - dp[u];
                self(self, v, u);
            }
        }
    };

    dfs(dfs, 1, 0);

    int l = 1, r = 1e9;
    for (int i = 2; i <= n; i++) {
        if (dep[i] % 2) {
            l = std::max(l, 1 - dp[i]);
            r = std::min(r, (int)1e9 - dp[i]);
        } else {
            l = std::max(l, dp[i] - (int)1e9);
            r = std::min(r, dp[i] - 1);
        }
    }

    if (l <= r) {
        std::cout << "YES\n";
        for (int i = 1; i <= n; i++) {
            if (dep[i] % 2) {
                std::cout << dp[i] + l << " \n"[i == n];
            } else {
                std::cout << dp[i] - l << " \n"[i == n];
            }
        }
    } else {
        std::cout << "NO\n";
    }
}

C. 中位数

题目大意

给定一个长度为奇数的数组,每次可以选择三个连续数字删去,然后插入一个数字,值为这三个数字的中位数,求最后在数组中剩下的数字的最大值是多少

解题思路

二分答案数组转01,目标是要尽可能去除0,而只要有两个0就必定能在这一组中去掉至少一个0,因此只需要检查是否插入了两个0即可,用栈维护,看最后在栈中是0的数量多还是1的数量多

代码实现

#include <bits/stdc++.h>

using i64 = long long;

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

    int tt;
    std::cin >> tt;

    while (tt--) {
        int n;
        std::cin >> n;

        std::vector<int> a(n);
        for (int i = 0; i < n; i++) {
            std::cin >> a[i];
        }

        auto check = [&](int x) -> bool {
            int cnt = 0;
            std::vector<int> stk;
            for (int i = 0; i < n; i++) {
                if (a[i] >= x) {
                    stk.push_back(1);
                } else {
                    stk.push_back(0);
                }
                cnt++;

                while (cnt >= 3 && !stk[cnt - 3] && !stk[cnt - 2]) {
                    stk.pop_back();
                    stk.pop_back();
                    stk.pop_back();
                    stk.push_back(0);
                    cnt -= 2;
                }
            }

            return std::count(stk.begin(), stk.end(), 1) * 2 >= cnt;
        };

        int l = 1, r = 1e9, ans = 0;
        while (l <= r) {
            int mid = (l + r) / 2;
            if (check(mid)) {
                ans = mid;
                l = mid + 1;
            } else {
                r = mid - 1;
            }
        }

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

E. 卡牌游戏

题目大意

给定一个偶数长度的数组,可以取出一个数字插到任意位置,然后按照奇偶位置把数组分为两组并获得和较小的那一组,问和的最大值是多少

解题思路

总和不变获得和较小的一组,其实就是想让两组的和接近,插入数字后可能会造成后序的奇偶发生变化,导致反转某一段区间内的符号。考虑枚举右端点,根据r的奇偶维护两个set来找最接近差值的l

代码实现

#include <bits/stdc++.h>

using i64 = long long;

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

    int tt;
    std::cin >> tt;

    while (tt--) {
        i64 n, sum = 0;
        std::cin >> n;

        std::vector<i64> a(2 * n + 1), odd(n + 1), even(n + 1);
        for (int i = 1; i <= 2 * n; i++) {
            std::cin >> a[i];
            sum += a[i];
            if (i % 2) {
                odd[(i + 1) / 2] = odd[(i + 1) / 2 - 1] + a[i];
            } else {
                even[i / 2] = even[i / 2 - 1] + a[i];
            }
        }

        std::set<i64> st[2];
        i64 ans = std::abs(odd[n] - even[n]);
        for (int i = 1; i <= 2 * n; i++) {
            i64 x = odd[n] - even[n] + 2 * (even[i / 2] - odd[(i + 1) / 2]);
            auto it = st[i % 2].lower_bound(x);
            if (it != st[i % 2].end()) {
                ans = std::min(ans, std::abs(x - *it));
            }
            if (it != st[i % 2].begin()) {
                ans = std::min(ans, std::abs(x - *--it));
            }
            st[i % 2].insert(2 * (even[i / 2] - odd[(i + 1) / 2]));
        }

        std::cout << (sum - ans) / 2 << "\n";
    }
}

H. 难以控制的滑板火箭

题目大意

给定一个n*m的01矩阵,0表示障碍1表示空地,每次可以朝周围八个方向移动,每分钟可以移动[l,r]次,问从(1,1)去往(n,m)最少需要多少分钟,无法到达输出-1

解题思路

首先bfs处理出最短路,然后分类讨论

  • \(l \neq r\)的时候答案显然是最短路除以r向上取整
  • \(l = r\)的时候且为奇数的时候,需要看耗时的奇偶性和最短路的奇偶性是否相同,否则需要增加一秒来浪费
  • \(l = r\)的时候且为偶数的时候,答案是偶数长度的最短路除以r向上取整

代码实现

#include <bits/stdc++.h>

using i64 = long long;

const int dx[] = {-1, -1, -1, 0, 0, 1, 1, 1};
const int dy[] = {-1, 0, 1, -1, 1, -1, 0, 1};

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

    int tt;
    std::cin >> tt;

    while (tt--) {
        int n, m;
        std::cin >> n >> m;

        int l, r;
        std::cin >> l >> r;

        std::vector<std::string> g(n);
        for (int i = 0; i < n; i++) {
            std::cin >> g[i];
        }

        std::vector<std::vector<std::vector<int>>> dist(n, std::vector<std::vector<int>>(m, std::vector<int>(2, INT_MAX)));
        std::queue<std::array<int, 3>> q;
        dist[0][0][0] = 0;
        q.push({0, 0, 0});

        while (!q.empty()) {
            auto [x, y, z] = q.front();
            q.pop();
            for (int i = 0; i < 8; i++) {
                int nx = x + dx[i], ny = y + dy[i];
                if (nx >= 0 && nx < n && ny >= 0 && ny < m && g[nx][ny] == '1' && dist[nx][ny][1 - z] == INT_MAX) {
                    dist[nx][ny][1 - z] = dist[x][y][z] + 1;
                    q.push({nx, ny, 1 - z});
                }
            }
        }

        i64 dist0 = dist[n - 1][m - 1][0], dist1 = dist[n - 1][m - 1][1];
        i64 time0 = (dist0 + r - 1) / r, time1 = (dist1 + r - 1) / r;
        if (l == r) {
            if (l % 2) {
                i64 t = INT_MAX;
                if (dist0 != INT_MAX) {
                    t = std::min(t, time0 + (i64)(time0 % 2 != dist0 % 2));
                }
                if (dist1 != INT_MAX) {
                    t = std::min(t, time1 + (i64)(time1 % 2 != dist1 % 2));
                }
                if (t == INT_MAX) {
                    std::cout << -1 << "\n";
                } else {
                    std::cout << t << "\n";
                }
            } else {
                if (dist0 == INT_MAX) {
                    std::cout << -1 << "\n";
                } else {
                    std::cout << time0 << "\n";
                }
            }
        } else {
            if (dist0 == INT_MAX && dist1 == INT_MAX) {
                std::cout << -1 << "\n";
            } else {
                std::cout << std::min(time0, time1) << "\n";
            }
        }
    }
}

I. 割点

题目大意

给定一个长度为n-2的01序列a,构造一张n点无向图,要求满足以下条件

  • 点1是割点,点n不是割点
  • \(a_i\)是1则点i是割点,否则i不是割点
  • 点的度数满足 \(deg_1 \geq deg_2 \geq ... \geq deg_n\)

输出任意一种构造方法即可,无法构造输出-1

解题思路

考虑点2~n的割点数量

  • 如果有两个及以上的非割点,一种较为简单的构造方法是一条链加上一个环,把环挂到1上,此时\(deg_1=3\)\(deg_n=1\),其余点度数都是2
  • 如果只有一个非割点,如果是点n-1则有解(直接挂1上),否则无解(度数不满足)
  • 如果没有非割点,无解(一条链的时候度数不满足要求)

代码实现

#include <bits/stdc++.h>

using i64 = long long;

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

    int tt;
    std::cin >> tt;

    while (tt--) {
        int n;
        std::cin >> n;

        std::string s;
        std::cin >> s;

        int cnt = std::count(s.begin(), s.end(), '0');
        if (cnt == 0 || cnt == 1 && s.back() != '0') {
            std::cout << -1 << "\n";
        } else {
            std::vector<int> link = {1}, circle;
            for (int i = 0; i < n - 2; i++) {
                if (s[i] == '1') {
                    link.push_back(i + 2);
                } else {
                    circle.push_back(i + 2);
                }
            }
            link.push_back(n);

            std::set<std::array<int, 2>> ans;
            for (int i = 0; i + 1 < link.size(); i++) {
                ans.insert({link[i], link[i + 1]});
            }
            for (int i = 0; i + 1 < circle.size(); i++) {
                ans.insert({circle[i], circle[i + 1]});
            }
            ans.insert({1, circle.front()});
            ans.insert({1, circle.back()});

            std::cout << ans.size() << "\n";
            for (auto [u, v] : ans) {
                std::cout << u << " " << v << "\n";
            }
        }
    }
}

L. 众数

题目大意

给定一个数组,对于这个数组长度为k的前缀,考虑一个集合S,定义f(S)是集合中最大值和最小值的和,求每种长度出现的\(2^k-1\)个f(S)的众数

解题思路

诈骗题,看似复杂但是考虑数字的种类就会发现,只有两种数字且最小值只出现了一次的时候答案才是min+max,其他情况都是max+max

代码实现

#include <bits/stdc++.h>

using i64 = long long;

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

    int tt;
    std::cin >> tt;

    while (tt--) {
        int n;
        std::cin >> n;

        std::map<int, int> mp;
        for (int i = 0; i < n; i++) {
            int x;
            std::cin >> x;
            mp[x]++;
            if (mp.size() == 2 && mp.begin()->second == 1) {
                std::cout << mp.rbegin()->first * 2 << " \n"[i == n - 1];
            } else {
                std::cout << mp.begin()->first + mp.rbegin()->first << " \n"[i == n - 1];
            }
        }
    }
}
posted @ 2025-07-09 03:26  udiandianis  阅读(472)  评论(0)    收藏  举报