2025牛客寒假算法基础集训营5

A

B

贴个代码就完了

CODE

点击查看代码
void solve()
{
    int n = 0, t = 0, k = 0;
    std::cin >> n >> t >> k;
    int ans = (n - k) / t;
    if (ans - 1 >= k) {
        ans = k + 1;
    }
    std::cout << ans << '\n';
}

C

题目大意

以 01 串的形式给出 3 个数 ABC。可以对 AB 进行的操作有:

  • x 的代价反转一位
  • 在一个数中选两位以 y 的代价交换这两位

问至少需要花费多少代价才能使得 AB=C

解题思路

首先容易想到当 2xy 时,全部选第一个操作就好了。否则应该优先选择操作 2,而且每次使用操作 2 时应该能同时使两位合法。

可以把所有不合法的位分为 4 类:

  • 0:在该位上 A 为 0,B 为 0;
  • 1:在该位上 A 为 0,B 为 1;
  • 2:在该位上 A 为 1,B 为 0;
  • 3:在该位上 A 为 1,B 为 1;

由于对于每个不和法的位,我们只需要在两个数中任选一个来反转该位就能使该位合法,所以上述 4 类不合法的位(包含了所有不合法的位),能够选择其他 3 类来进行操作 2。比如第 0 类和第 1 类就在 B 上做交换,第 0 类和第 2 类就在 A 上做交换。

所以问题又变成了有 4 种颜色的小球,不同颜色的小球间可以两两配对,求最多能配多少对。
这种问题不管有几种颜色,解法都是统一的,即先看数量最多的球是否超过到总数的一半,超过就其他的球都和它配对,最后还有剩的就不能再配对了。否则配对数直接就是总数除 2 向下取整,如果总数是奇数最后就会剩下一个球。

知道了能进行多少次交换操作后,剩下的就都是操作 1 了。

CODE

点击查看代码
void solve()
{
    i64 n = 0, x = 0, y = 0;
    std::cin >> n >> x >> y;
    std::string a, b, c;
    std::cin >> a >> b >> c;
    std::vector cnt(4, 0ll);
    for (int i = 0; i < n; i++) {
        if (c[i] == '0') {
            if (a[i] == '0' && b[i] == '1') {
                cnt[1]++;
            }
            else if (a[i] == '1' && b[i] == '0') {
                cnt[2]++;
            }
        }
        else {
            if (a[i] == '0' && b[i] == '0') {
                cnt[0]++;
            }
            else if (a[i] == '1' && b[i] == '1') {
                cnt[3]++;
            }
        }
    }

    i64 sum = cnt[0] + cnt[1] + cnt[2] + cnt[3];
    if (x * 2 <= y) {
        std::cout << x * sum << '\n';
    }
    else {
        std::sort(cnt.begin(), cnt.end());
        if (cnt[3] * 2 >= sum) {
            i64 num = sum - cnt[3];
            std::cout << y * num + x * (cnt[3] - num) << '\n';
        }
        else {
            std::cout << y * (sum / 2) + x * (sum % 2) << '\n';
        }
    }

    return;
}

D

题目大意

在 01 数组 A 中,从第一个数开始,每 k 个数划分一段,如果最后一段不足 k 个数的也算作一段,在划分出来的每一段内,可以进行如下操作任意顺序任意次:

  • 将段内所有的数 01 互换。
  • 任意排列段内的数。

将操作后得到的序列中所有连续相同的数都变成一个数后得到一个 01 相间的序列,我们定义 f(k) 为最后的到的这个序列长度的最小值。

i=1nf(i)

解题思路

根据题目描述的操作,我们要知道如果某一段中有若干数量大于 0 的 0 和 1,我们就可以把这段看成 10,如果某一段中只有一个数,不管是 0 还是 1 我们都可以把这段看成 00。于是接下来我们考虑,x10 拼成的序列通过合并连续相同的数最短能达到多长。答案是 x+1(比如有 3 段,就这样拼 100110)。再考虑如果 x 段中有 y10 变成了 00,能达到的最短长度是多少。答案是 x+1y

于是我们就可以这么来处理:先令 f(i)=ni+1,即假设分成的所有段都可以看成 10。然后再去计算每段长度为 i 时有多少段全是相同的数,初始的值减去这个数量就是我们要的精确的值了。注意一下最后一段的处理就好了。时间复杂度是线性的。

CODE

点击查看代码
void solve()
{
    int n = 0;
    std::string s;
    std::cin >> n;
    std::cin >> s;
    std::vector ans(n + 1, 1);
    for (int i = 1; i <= n; i++) {
        ans[i] += (n + i - 1) / i; // n / i 向上取整
    }

    for (int l = 0, r = 0; l < n; l = r) {
        while (r < n && s[l] == s[r]) {
            r++;
        }
        int len = r - l;
        for (int k = 1; k <= len; k++) {
            int ll = (l + k - 1) / k * k;
            ans[k] -= (r - ll) / k;
            if (r == n && (r - ll) % k != 0) {
                ans[k]--;
            }
        }
        if (r == n) {
            for (int k = len + 1; k <= n; k++) {
                if ((l + k - 1) / k * k < r) {
                    ans[k]--;
                }
            }
        }
    }

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

E

题目大意

给定一个未分出胜负的三子棋局面,且双方走了相同的步数。问让白子先走两步白子能否必胜。

解题思路

当各走走了 2 步以下时,白子是必胜的。

当各走了 4 步时,由于只剩下一个位置,所以下完检查以下就好了。

当各走了 3 步,还剩下 3 个位置,C32 枚举所有情况即可。

CODE

点击查看代码
bool chk() {
    for (int i = 0; i < 3; i++) {
        if (g[i] == "XXX") {
            return true;
        }
    }

    for (int j = 0; j < 3; j++) {
        bool line = true;
        for (int i = 0; i < 3; i++) {
            if (g[i][j] != 'X') {
                line = false;
                break;
            }
        }
        if (line) {
            return true;
        }
    }

    if (g[1][1] == 'X' && g[0][0] == 'X' && g[2][2] == 'X') {
        return true;
    }
    if (g[1][1] == 'X' && g[0][2] == 'X' && g[2][0] == 'X') {
        return true;
    }

    return false;
}

void chg(std::array<int, 2> p, char c) {
    g[p[0]][p[1]] = c;
}

bool solve()
{
    int cnt = 0;
    std::vector<std::array<int, 2>> pos;
    for (int i = 0; i < 3; i++) {
        std::cin >> g[i];
        for (int j = 0; j < 3; j++) {
            if (g[i][j] == 'X') {
                cnt++;
            }
            else if (g[i][j] == 'G') {
                pos.push_back({ i, j });
            }
        }
    }

    if (cnt <= 2) {
        return true;
    }
    else if (cnt == 4) {
        chg(pos[0], 'X');
        return chk();
    }
    else {
        for (int i = 0; i < 3; i++) {
            chg(pos[i], 'X'), chg(pos[(i + 1) % 3], 'X');
            if (chk()) {
                return true;
            }
            chg(pos[i], 'G'), chg(pos[(i + 1) % 3], 'G');
        }
    }

    return false;
}

H

题目大意

将一个长度为 n 的数组划分成 k 段,求在所有的划分方案中,所有区间的区间最大值乘区间最小值之和。

解题思路

这种题一般是要单独算贡献。所以直接枚举所有区间,再看有多少划分方案是包含这个区间的就好了。用隔板法考虑,注意下当枚举到的区间是最左边或者最右边的情况。

CODE

点击查看代码
void solve()
{
    int n = 0, k = 0;
    std::cin >> n >> k;
    C[0][0] = 1;
    for (int i = 1; i <= n; i++) {
        C[i][0] = 1;
        for (int j = 1; j <= i; j++) {
            C[i][j] = (C[i - 1][j] + C[i - 1][j - 1]) % Mod;
        }
    }

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

    for (int l = 1; l < L; l++) {
        for (int i = 0; i + (1 << l) <= n; i++) {
            st[i][l][0] = std::max(st[i][l - 1][0], st[i + (1 << l - 1)][l - 1][0]);
            st[i][l][1] = std::min(st[i][l - 1][1], st[i + (1 << l - 1)][l - 1][1]);
        }
    }
    // 左闭右开
    auto quiry = [&](int l, int r) -> i64 {
        int len = log2(r - l);
        int m = r - (1 << len);
        return std::max(st[l][len][0], st[m][len][0]) * std::min(st[l][len][1], st[m][len][1]) % Mod;
    };

    i64 ans = 0;
    if (k == 1) {
        ans = quiry(0, n);
    }
    else if (k == 2) {
        for (int i = 1; i < n; i++) {
            (ans += (quiry(0, i) + quiry(i, n)) % Mod) %= Mod;
        }
    }
    else {
        for (int len = 1; len + k - 1<= n; len++) {
            for (int i = 0; i + len <= n; i++) {
                int d = (i == 0 || i + len == n) ? 1 : 2;
                (ans += quiry(i, i + len) * C[n - len - d][k - 1 - d] % Mod) %= Mod;
            }
        }
    }
    std::cout << ans << '\n';
    return;
}

I

相等判 YES,否则看有没有 0。

CODE

点击查看代码
bool solve()
{
    int n = 0, m = 0;
    std::cin >> n >> m;
    if (n == m) {
        return true;
    }
    else if (n == 0 || m == 0) {
        return false;
    }
    else {
        return true;
    }
}

J

题目怎么说就怎么做

CODE

点击查看代码
void solve()
{
    int n = 0;
    std::string s;
    std::cin >> n >> s;
    i64 ans = 0;
    int v = 0;
    for (auto &c : s) {
        if (c == '0') {
            v += 10;
            ans += v;
        }
        else if (c == '1') {
            v = std::max(0,  v - 5);
            ans += v;
        }
        else {
            ans += std::max(0, v - 10);
        }
    }
    std::cout << ans << '\n';
}

K

题目大意

在平面上有若干个点,点 xi,yi 对总价值的贡献是平面上存在的到该点距离为 ri 的点数量。求总价值。

解题思路

除了横或纵坐标相同的点外,还需要赵勾股数满足 a2+b2=ri2,于是先把所有的勾股数找到就好了。

(感觉找勾股数可以单独写一篇博客

CODE

点击查看代码
constexpr int N = 1e5, M = 2e5, Inf = 1e9, MXR = 3e5;

std::vector<std::array<int, 2>> R[MXR + 5];
std::unordered_set<int> mp[M + 5];

int ans;
void add(int x, int y) {
    if (x >= 0 && y >=0 && x <= M && y <= M && mp[x].count(y) != 0) {
        ans++;
    }
}

void solve()
{
    for (int i = 1; i * i <= MXR; i++) {
        for (int j = 1; j < i && i * i + j * j <= MXR; j++) {
            if ((i + j) % 2 == 0 || std::__gcd(i, j) != 1) {
                continue;
            }

            int a = 2 * i * j;
            int b = i * i - j * j;
            int c = i * i + j * j;
            for (int k = 1; c * k <= MXR; k++) {
                R[c * k].push_back({ a * k, b * k });
            }
        }
    }

    int n = 0;
    std::cin >> n;
    std::vector p(n, std::array<int, 3>{});
    for (auto &[x, y, r] : p) {
        std::cin >> x >> y >> r;
        x += N, y += N;
        mp[x].insert(y);
    }

    for (auto &[x, y, r] : p) {
        if (r > MXR) {
            continue;
        }
        add(x + r, y);
        add(x - r, y);
        add(x, y + r);
        add(x, y - r);
        for (auto &[d1, d2] : R[r]) {
            add(x + d1, y + d2);
            add(x + d1, y - d2);
            add(x - d1, y + d2);
            add(x - d1, y - d2);
            add(x + d2, y + d1);
            add(x + d2, y - d1);
            add(x - d2, y + d1);
            add(x - d2, y - d1);
        }
    }
    std::cout << ans << '\n';
}

L

构造。

我观察到的是从 1 开始以数 6 个为一组,每组可以产生两个符合条件的三元组。

n 不为 3 且模 6 余 3 时,首尾处要特殊搞一下,但中间还是 6 个 6 个一组。

CODE

点击查看代码
void solve()
{
    int n = 0;
    std::cin >> n;
    int ans = n / 6;
    if (n <= 3) {
        std::cout << 0 << '\n';
    }
    else if (n % 6 == 3) {
        std::cout << ans * 2 + 1 << '\n';
        std::cout << 1 << ' ' << 3 << ' ' << n << '\n';
        std::cout << 2 << ' ' << 4 << ' ' << 5 << '\n';
        std::cout << n - 1 << ' ' << n - 2 << ' ' << n - 3 << '\n';
        for (int i = 6; i < n - 3; i += 6) {
            std::cout << i << ' ' << i + 1 << ' ' << i + 3 << '\n';
            std::cout << i + 2 << ' ' << i + 4 << ' ' << i + 5 << '\n';
        }
    }
    else {
        int d = 0;
        if (n % 6 >= 4) {
            d = 1;
        }
        std::cout << ans * 2 + d << '\n';
        for (int i = 1; i < ans * 6; i += 6) {
            std::cout << i << ' ' << i + 1 << ' ' << i + 3 << '\n';
            std::cout << i + 2 << ' ' << i + 4 << ' ' << i + 5 << '\n';
        }
        if (d == 1) {
            int i = ans * 6 + 1;
            std::cout << i << ' ' << i + 1 << ' ' << i + 3 << '\n';
        }
    }
    return;
}
posted @   Young_Cloud  阅读(1)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
点击右上角即可分享
微信分享提示