CCPC 2024 山东省赛

I. Left Shifting

题目大意

给一个字符串,每次操作可以取最左边的字符放在最右边,问最少操作多少次能让首尾字符相等,无法实现输出-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--) {
        std::string s;
        std::cin >> s;

        int ans = -1, n = s.size();
        for (int i = 1; i < n; i++) {
            if (s[i] == s[i - 1]) {
                ans = i;
                break;
            }
        }

        if (s.front() == s.back()) {
            ans = 0;
        }

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

A. Printer

题目大意

n台打印机需要k份文件,每t秒可以打印一份,打印l份后需要休息w秒,问至少需要多久才能完成打印

解题思路

文件数量和时间存在单调关系,所以可以二分答案,check途中需要注意提前退出,否则会爆longlong

代码实现

#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, k;
        std::cin >> n >> k;

        std::vector<std::array<i64, 3>> tlw(n);
        for (int i = 0; i < n; i++) {
            std::cin >> tlw[i][0] >> tlw[i][1] >> tlw[i][2];
        }

        auto check = [&](i64 x) -> bool {
            i64 sum = 0;
            for (auto [t, l, w] : tlw) {
                sum += x / (t * l + w) * l + std::min(x % (t * l + w) / t, l);
                if (sum >= k) {
                    return true;
                }
            }
            return sum >= k;
        };

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

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

K. Matrix

题目大意

给定一个n,要求构造出一个n*n的矩阵,1-2n的元素至少出现一次,要求矩阵只存在一个由两条水平线和两条垂线交点组成的四元组,四个的元素互不相等

解题思路

显然让这些水平线和垂线都由一个元素构成就能满足,一种较为简单的构造方法是,让4个不同的数字在左上角形成一个2*2的矩阵,接下来往右边填充n-2个数字列,最后填充剩下n-2个数字行,刚好用完2n个数

代码实现

#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::cout << "Yes\n";
    std::vector<std::vector<int>> ans(n, std::vector<int>(n));
    ans[0][0] = 1;
    ans[0][1] = 2;
    ans[1][0] = 3;
    ans[1][1] = 4;
    for (int i = 2; i < n; i++) {
        ans[0][i] = 3 + i;
        ans[1][i] = 3 + i;
    }

    int x = ans[0].back() + 1;
    for (int i = 2; i < n; i++) {
        for (int j = 0; j < n; j++) {
            ans[i][j] = x;
        }
        x++;
    }

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < n; j++) {
            std::cout << ans[i][j] << " \n"[j == n - 1];
        }
    }
}

F. Divide the Sequence

题目大意

给你一个数组,可以把数组分成k个子串,第i份的权重为i,最后的贡献为权重乘上第i份的和,问k为1到n的最大贡献

解题思路

显然只要每次贪心地取最大的后缀和累加即可,注意只用对后n-1个后缀排序,因为k=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--) {
        i64 n, ans;
        std::cin >> n;

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

        for (int i = n - 2; i >= 0; i--) {
            suff[i] += suff[i + 1];
        }
        
        std::sort(suff.begin() + 1, suff.end());

        ans = suff[0];
        std::cout << ans << " \n"[n == 1];
        for (int i = n - 1; i >= 1; i--) {
            ans += suff[i];
            std::cout << ans << " \n"[i == 1];
        }
    }
}

C. Colorful Segments 2

题目大意

给n个lr区间,有交集的区间不能用相同的颜色,一共有k种颜色,问有多少种染色方案,对答案模998244353

解题思路

一个区间的染色数量会被影响就是看他和多少个区间有交集,可以对区间的l排序,然后维护r,每次都看当前这个区间和多少区间有交集,k减去交集的数量就是可以染的颜色数量,记得最后对答案再去一次模,要考虑只有一个区间但是k超过模数的情况

代码实现

#include <bits/stdc++.h>

using i64 = long long;
const int MOD = 998244353;

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, k;
        std::cin >> n >> k;

        std::vector<std::array<int, 2>> lr(n);
        std::priority_queue<std::array<int, 2>, std::vector<std::array<int, 2>>, std::greater<>> pq;
        for (int i = 0; i < n; i++) {
            std::cin >> lr[i][0] >> lr[i][1];
        }

        std::sort(lr.begin(), lr.end());

        i64 sum = k, cnt = 1;
        for (int i = 0; i < n; i++) {
            if (!pq.empty()) {
                while (!pq.empty()) {
                    auto [r, l] = pq.top();
                    while (r < lr[i][0] && !pq.empty()) {
                        pq.pop();
                        r = pq.top()[0];
                    }
                    break;
                }
                sum = (sum * std::max(0ll, k - (i64)pq.size())) % MOD;
            }
            pq.push({lr[i][1], lr[i][0]});
        }
        
        std::cout << sum % MOD << "\n";
    }
}

J. Colorful Spanning Tree

题目大意

给n个颜色数量,第i个表示颜色为i的节点数量。再给一个邻接矩阵,\(g[i][j]\) 表示i颜色和j颜色连边的权值,问这些节点构成的最小生成树权值和是多少

解题思路

题目的数据范围很大,硬跑最小生成树显然不可以,但是颜色数量比较少,可以从此入手。考虑所有颜色都只有一个节点的情况,发现答案就是最小生成树权值和,同时发现复杂度允许 \(n^2\) 暴力维护每一个颜色最小权值的连边颜色,因此只需要跑一遍最小生成树,剩下的同色都挂当前颜色最小颜色节点上即可

代码实现

#include <bits/stdc++.h>

using i64 = long long;

struct DSU {
    std::vector<int> fa, rank, siz;
    int cnt;

    DSU(int n) : fa(n + 1), rank(n + 1), siz(n + 1, 1), cnt(n) {
        for (int i = 1; i <= n; i++) {
            fa[i] = i;
        }
    }

    int find(int x) {
        if (fa[x] != x) {
            fa[x] = find(fa[x]);
        }
        return fa[x];
    }

    void merge(int x, int y) {
        int X = find(x), Y = find(y);
        if ((X != Y)) {
            siz[X] += siz[Y];
            if (rank[X] >= rank[Y]) {
                fa[Y] = X;
                rank[X] += (int)(rank[X] == rank[Y]);
            } else {
                fa[X] = Y;
            }
            cnt--;
        }
    }

    int size() {
        return cnt;
    }

    int count(int x) {
        return siz[find(x)];
    }
};

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, ans = 0;
        std::cin >> n;

        std::vector<i64> a(n + 1), minw(n + 1, LONG_LONG_MAX);
        for (int i = 1; i <= n; i++) {
            std::cin >> a[i];
        }

        DSU dsu(n);
        std::vector<std::array<i64, 3>> g;
        for (int u = 1; u <= n; u++) {
            for (int v = 1; v <= n; v++) {
                i64 w;
                std::cin >> w;
                minw[u] = std::min(minw[u], w);
                if (u != v) {
                    g.push_back({w, u, v});
                }
            }
        }

        std::sort(g.begin(), g.end());

        for (auto [w, u, v] : g) {
            if (dsu.find(u) != dsu.find(v)) {
                ans += w;
                dsu.merge(u, v);
            }
        }

        for (int i = 1; i <= n; i++) {
            ans += (a[i] - 1) * minw[i];
        }

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

D. Hero of the Kingdom

题目大意

买入x件商品需要ax+b秒和px金币,卖出x件商品需要cx+d秒但可以赚qx金币。有t秒以及m金币,问t秒结束后最多能有多少金币

解题思路

首先考虑到的显然是每次尽可能多的买,但是当花费时间和利润很低t很大的时候会被卡到接近O(t)的复杂度,不难发现如果m/p的值一直都是相同的,那么操作流程都是一样的,因此可以直接算出让m/p发生变化需要购买的数量,具体操作见代码和注释

代码实现

#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 p, a, b, q, c, d, m, t;
        std::cin >> p >> a >> b >> q >> c >> d >> m >> t;

        while (t > 0) {
            i64 X1 = m / p;  // 一次最多买多少
            if (X1 == 0) {
                break;
            }
            i64 X2 = std::max(X1, ((X1 + 1) * p - m + q - p - 1) / (q - p));  // 让m/p增加1要买多少
            i64 cnt = (X2 + X1 - 1) / X1;                                     // 要买多少次
            X2 = std::max(X2, X1 * cnt);
            i64 T = X2 * (a + c) + cnt * (b + d);
            if (t >= T) {
                t -= T;
                m += X2 * (q - p);
            } else {
                cnt = t / (X1 * (a + c) + b + d);  // 剩余时间内最多买多少次
                X2 = cnt * X1;
                T = X2 * (a + c) + cnt * (b + d);
                t -= T + b + d;
                m += X2 * (q - p);
                if (t > 0) {
                    m += t / (a + c) * (q - p);
                }
                break;
            }
        }

        std::cout << m << "\n";
    }
}
posted @ 2025-03-09 22:27  udiandianis  阅读(245)  评论(0)    收藏  举报