codeforces/CF 1850ABCDEFGH题解

A

题意

需要检查一个包含3个整数的数组,是否存在两个数之和大于等于10。

题解

主要思路是:

  1. 计算数组所有元素之和sum
  2. 遍历数组,检查sum减去当前元素是否>= 10
  3. 如果找到满足的元素,设置found=true,退出循环
  4. 根据found的值输出结果

代码

#include <bits/stdc++.h>

using namespace std;

int main() {

    int T;
    cin >> T;

    while(T--) {

        int a[3];
        for(int i = 0; i < 3; i++) {
            cin >> a[i]; 
        }

        bool found = false;
        int sum = accumulate(a, a + 3, 0);

        for(int i = 0; i < 3; i++) {
            if(sum - a[i] >= 10) {
                found = true;
                break;
            }
        }

        if(found) {
            cout << "YES" << endl;
        }
        else {
            cout << "NO" << endl;
        }

    }

    return 0;
}

B

题意

给出若干个(a, b)对,找出a<=10中b最大的下标

题解

  1. 读入测试用例的个数T,对每个测试用例循环
  2. 读入对的个数n,创建一个元组(a, b, i)的向量,其中i是对的下标
  3. 使用std::partition将向量分成两部分:第一部分包含所有a<=10的元组,第二部分包含剩余的元组
  4. 使用std::max_element在第一部分的向量中找到b最大的元组
  5. 输出找到的元组的下标i加一(因为题目中的下标是从1开始的)

代码

#include <bits/stdc++.h>

typedef std::pair<int, int> pii;

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

    int T;
    std::cin >> T;

    while (T--)
    {
        int n;
        std::cin >> n;
        std::vector<std::tuple<int, int, int>> a(n);
        int length, quality;
        for (int i = 0; i < n; i++) {
            std::cin >> length >> quality;
            a[i] = std::make_tuple(length, quality, i);
        }

        auto iter = std::partition(a.begin(), a.end(), [](auto it)
                                   { return std::get<0>(it) <= 10; });
        auto max_iter = std::max_element(a.begin(), iter, [&](const auto &a, const auto &b)
                                         { return std::get<1>(a) < std::get<1>(b); });

        std::cout << std::get<2>(*max_iter) + 1 << '\n';
    }
}

C

题意

在 8*8 的字符矩阵中有一列中存在连续小写字母存在的单词,其余皆为 *,找出其中的单词

题解

  1. 读入8个字符串,表示矩阵的每一行,存储在一个向量中
  2. 初始化一个空字符串res,用来存储结果
  3. 遍历向量中的每个字符串和每个字符
  4. 如果字符是小写字母,就把它加到res后面
  5. 输出res作为答案

代码

#include <bits/stdc++.h>

typedef std::pair<int, int> pii;

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

    int T;
    std::cin >> T;

    while (T--)
    {
        std::vector<std::string> s(8);
        for (auto &it : s)
            std::cin >> it;

        std::string res;
        for (auto str : s)
            for (char ch : str) 
                if(islower(ch))
                    res.push_back(ch);
        
        std::cout << res << '\n';
    }
}

D

题意

移除尽量少的元素使得数列中所有连续的元素之差小于等于 k

题解

  1. 首先,对输入的数列进行排序,使得相邻的元素之差最小。
  2. 然后,用一个双指针法遍历数列,找出最长的连续子序列,使得其中任意两个元素之差不超过k。
  3. 最后,用数列的长度减去最长子序列的长度,就是需要移除的元素个数。

代码

#include <bits/stdc++.h>

typedef std::pair<int, int> pii;

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

    int T;
    std::cin >> T;

    while (T--)
    {
        int n, k;
        std::cin >> n >> k;
        std::vector<int> a(n);
        for (auto &it : a)
            std::cin >> it;
        sort(a.begin(), a.end());

        int res = INT_MIN;
        for (int j = 0; j < n;)
        {
            int p = j;
            while (++j < n && a[j] - a[j - 1] <= k)
                ;
            res = std::max(res, j - p);
        }
        std::cout << n - res << '\n';
    }
}

E

题意

将 n 个边长为 a_i 的正方形,给每个正方形边长增加到 a_i + 2*w,使得所有正方形面积之和小于等于 c

题解

  1. 使用二分查找法来找出最大的满足条件的w值,即所有正方形的面积之和不超过c。
  2. 定义一个check函数来判断给定的w值是否满足条件,即计算所有正方形的新面积之和,并与c进行比较。
  3. 在主函数中,对数列进行排序,并初始化二分查找的左右边界和答案。
  4. 在二分查找的过程中,每次取中间值作为w值,并调用check函数来判断是否满足条件,根据结果更新左右边界和答案。

代码

#include <bits/stdc++.h>

typedef std::pair<int, int> pii;
typedef long long LL;

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

    int T;
    std::cin >> T;

    while (T--)
    {
        int n;
        LL s;
        std::cin >> n >> s;
        std::vector<LL> a(n);
        for (auto &it : a)
            std::cin >> it;
        int left = 1, right = 1e9 / 2, mid, ans;

        auto check = [&](LL mid) -> bool
        {
            LL res = 0;
            mid <<= 1;
            for (auto it : a)
            {
                res += (mid + it) * (mid + it);
                if (res > s)
                    return false;
            }
            return true;
        };

        while (left <= right)
        {
            mid = (left + right) >> 1;
            if (check(mid))
            {
                left = mid + 1;
                ans = mid;
            }
            else
                right = mid - 1;
        }
        std::cout << ans << '\n';
    }
}

F

题意

有 n 只青蛙,每只青蛙以 a_i 为步长不停得跳,在 [1, n] 中设置一个陷阱,捕捉尽可能多得青蛙,问做多能抓几只青蛙

题解

  1. 首先,观察到如果青蛙的步长为 a,那么它只能跳到 a 的倍数的位置,所以我们可以按照步长来分组,统计每个步长有多少只青蛙。
  2. 然后,对于每个位置 i,我们可以遍历它的所有因子 j,并累加对应步长为 j 的青蛙的数量,得到在位置 i 可以捕捉到的青蛙的总数。
  3. 最后,我们在所有位置中找出最大的青蛙数量,就是答案。

复杂度为[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iUrS9HAN-1690212116810)(https://cdn.nlark.com/yuque/__latex/e45d75731139a9fda4a629426bf1f9ac.svg#card=math&code=n%2Alog%28n%29&id=Y0WpK)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y9BuBzYY-1690212116812)(https://cdn.nlark.com/yuque/__latex/1628a4ea7644802f0675c699b8e8320e.svg#card=math&code=%5Cfrac%7Bn%7D%7B1%7D%2B%5Cfrac%7Bn%7D%7B2%7D%2B%5Ccdots%2B%5Cfrac%7Bn%7D%7Bn%7D&id=EnBuL)] 这是著名的调和级数等于 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zoWblqHE-1690212116813)(https://cdn.nlark.com/yuque/__latex/dfbe0f7fcfb94e894b5c8502532f2797.svg#card=math&code=n%2Aln%28n%29&id=rscJV)]

代码

#include <bits/stdc++.h>

typedef std::pair<int, int> pii;
typedef long long LL;

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

    int T;
    std::cin >> T;

    while (T--)
    {
        int n, x;
        std::cin >> n;
        std::vector<int> a(n + 1), c(n + 1);
        for (int i = 1; i <= n; i++)
        {
            std::cin >> x;
            if (x <= n)
                a[x]++;
        }

        for (int i = 1; i <= n; i++)
            for (int j = i; j <= n; j += i)
                c[j] += a[i];
        
        std::cout << *std::max_element(c.begin(), c.end()) << '\n';
    }
}

G

题意

有 n 个二维点,问有几个二维点对,连成的线为8个方向之一
image.png

题解

  1. 首先,观察到如果两个点连成的线为8个方向之一,那么它们必须满足以下四种情况之一:
    1. 它们的横坐标相同,也就是说,它们在同一条竖直线上。
    2. 它们的纵坐标相同,也就是说,它们在同一条水平线上。
    3. 它们的横坐标和纵坐标之和相同,也就是说,它们在同一条从左下到右上的对角线上。
    4. 它们的纵坐标减去横坐标之差相同,也就是说,它们在同一条从左上到右下的对角线上。
  2. 然后,我们可以用四个数组来分别存储每个点的横坐标、纵坐标、横纵坐标之和、横纵坐标之差,并对每个数组进行排序。
  3. 接着,我们可以用一个函数来计算每个数组中有多少对相同的元素,也就是说,有多少对点满足其中一种情况。这个函数的思路是:
    1. 遍历数组中的每个元素,并用一个变量来记录当前元素出现的次数。
    2. 如果遇到不同的元素,就计算当前元素出现次数的二项式系数,也就是从中选两个的组合数,并累加到结果中。
    3. 如果遍历到数组的末尾,也要计算最后一个元素出现次数的二项式系数,并累加到结果中。
  4. 最后,我们把四个数组分别调用这个函数得到的结果相加,就是答案。

代码

#include <bits/stdc++.h>

typedef std::pair<int, int> pii;
typedef long long LL;

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

    int T;
    std::cin >> T;

    while (T--)
    {
        auto cal = [](std::vector<int> vec) -> LL
        {
            std::sort(vec.begin(), vec.end());

            int n = (int)vec.size();
            LL res = 0;
            for (int j = 0; j < n;)
            {
                int p = j;
                while (++j < n && vec[j] == vec[j - 1])
                    ;

                LL len = j - p;
                res += len * (len - 1);
            }
            return res;
        };

        int n;
        std::cin >> n;
        std::vector<pii> points(n);
        for (auto &point : points)
            std::cin >> point.first >> point.second;

        std::vector<int> x, y, xplusy, ysubx;
        std::transform(points.begin(), points.end(), std::back_inserter(x), [](const pii &point)
                       { return point.first; });
        std::transform(points.begin(), points.end(), std::back_inserter(y), [](const pii &point)
                       { return point.second; });
        std::transform(points.begin(), points.end(), std::back_inserter(xplusy), [](const pii &point)
                       { return point.first + point.second; });
        std::transform(points.begin(), points.end(), std::back_inserter(ysubx), [](const pii &point)
                       { return point.second - point.first; });

        LL res = cal(x) + cal(y) + cal(xplusy) + cal(ysubx);
        std::cout << res << '\n';
    }
}

H

题意

已知 n 个横坐标轴上点 a_i,和 m 个约束
每个约束 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UWSk2lAa-1690212116814)(https://cdn.nlark.com/yuque/__latex/6dc3974d55cff8726ef9c61a7a825894.svg#card=math&code=a_x%20-%20a_y%20%3D%20d&id=KU9og)], 问是否存在 n 个点满足所有约束

题解

  1. 首先,把每个约束看作一条有向边,从点 x 指向点 y,边的权值为 d,表示 a_x - a_y = d。这样就可以把问题转化为一个图论问题,即判断这个图是否存在环,并且环上的边权之和是否为0。
  2. 然后,我们可以用一个深度优先搜索(DFS)算法来遍历这个图,同时记录每个点的距离(即 a_i 的值),并检查是否有矛盾的情况出现。具体地,我们可以用一个数组 vis 来标记每个点是否被访问过,用一个数组 dis 来存储每个点的距离,用一个函数 dfs 来实现DFS算法。

代码

#include <bits/stdc++.h>

typedef std::pair<int, int> pii;
typedef long long LL;

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

    int T;
    std::cin >> T;

    while (T--)
    {
        int n, m;
        std::cin >> n >> m;
        std::vector<std::vector<pii>> E(n + 1);
        for (int i = 1; i <= m; i++)
        {
            int u, v, d;
            std::cin >> u >> v >> d;
            E[u].push_back(std::make_pair(v, d));
            E[v].push_back(std::make_pair(u, -d));
        }

        bool flag = true;
        std::vector<bool> vis(n + 1);
        std::vector<LL> dis(n + 1);
        std::function<void(int, LL)> dfs;
        dfs = [&](int now, LL w)
        {
            vis[now] = true;
            dis[now] = w;
            for (auto item : E[now])
            {
                int &v = item.first;
                int &d = item.second;
                if (!vis[v])
                    dfs(v, w + d);
                else
                {
                    if (dis[v] - dis[now] != d)
                        flag = false;
                }
            }
        };

        for (int i = 1; i <= n; i++)
            if (!vis[i])
                dfs(i, 0);

        std::cout << (flag ? "YES\n" : "NO\n");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值