Codeforces Round 1016 (Div. 3) A - E 详细题解

本文为Codeforces Round 1016 (Div. 3) A - E 详细题解,觉得有帮助或者写的不错可以点个攒

目录

题目A:

题目大意:

解题思路:

代码(C++):

题目B:

题目大意:

解题思路:

代码(C++):

题目C:

题目大意:

解题思路:

代码(C++):

题目D:

解题思路:

代码(C++):

题目E:

题目大意:

解题思路:

代码(C++):


题目A:

Problem - A - Codeforces

题目大意:

给你一个数字k,你可以生成任意的长度为k的回文数组a

现在你需要判断,对于所有的n满足(n >= k)有sum(a) = n,也就是a的所有元素和为n

通俗的讲:

生成一个长度为k的回文数组a,在保证回文的情况下,改变里面的元素值,保证元素和可以构成任意的数字n,其中(n >= k)

解题思路:

当k为奇数的时候,也就是中间的数字是单独的,我们可以每次只把中间那个数字增加1,这样就可以生成任意的数字和n(n >= k)

当k为偶数的时候,我们要保证数组是回文的,可以注意到,数组的元素和肯定为偶数,无法满足

代码(C++):

void solve() {
    int k;
    std::cin >> k;

    std::cout << ((k % 2) ? "YES" : "NO") << "\n";
}

题目B:

Problem - B - Codeforces

题目大意:

定义一个数字num的成本为:num / sum(num), sum(num)为数字各个数位的和

现在给出一个数字,每次操作中,你可以:

选择一个数位然后删除掉,直到数字变成个位数,但是不能为0,删除后可以有前导0

你需要求出最小的操作次数,使得数字的成本最小

解题思路:

我们观察测试用例3:
102030

输出的是:3,也就是我们可以删除1,2和3后面的0这三个数字,是数字变成003,成本为1,最小

那么我们可以注意到,要使得成本最小,数字后面的0必须删除,然后把最后一个不为0的数字保留,把前面所有的非0数字删除,剩下的0就变成前导0。

代码(C++):

void solve() {
    std::string s;
    std::cin >> s;

    int ans = 0;
    int idx = s.size() - 1;
    while (s[idx] == '0') {
        idx--;
        ans++;
    }

    for (int i = 0; i < idx; i++) {
        if (s[i] != '0') {
            ans++;
        }
    }

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

题目C:

Problem - C - Codeforces

题目大意:

给你两个数字,n和k,现在把n“重复”k次变成数字a,也就是比如n = 43, k = 2, 重复k次后得出的a = 4343。

现在你需要判断a是否为质数

解题思路:

首先k == 1的时候,我们只需要判断x是否是质数即可

然后当k > 1的时候,我们可以注意到:除了x = 1, k = 2也就是生成的数字为11的时候,为质数,其他时候都不为质数

因为肯定可以有原来数字作为生成数字的因数,比如4343肯定有43这个因数

小知识:19个1组成的数字是质数,23个1组成的数字是质数,317也是..但是此题的k <= 7,无法生成几种长度的由1组成的数字

代码(C++):

bool is_prime(int n) {
    if (n <= 1) return false;
    if (n == 2 || n == 3) return true;
    if (n % 2 == 0 || n % 3 == 0) return false;
    int i = 5;
    int w = 2;
    while (i * i <= n) {
        if (n % i == 0) return false;
        i += w;
        w = 6 - w;
    }
    return true;
}

void solve() {
    int x, k;
    std::cin >> x >> k;

    if (k == 1) {
        std::cout << (is_prime(x) ? "YES" : "NO") << "\n";
    } else {
        if (x == 1 && k == 2) {
            std::cout << "YES\n";
        } else {
            std::cout << "NO\n";
        }
    }
}

题目D:

Problem - D - Codeforces

解题思路:

“模拟”题,贴个我的代码吧

代码(C++):

#include <bits/stdc++.h>

using i64 = long long;

i64 dfs1(int n, int r, int c) {
    if(n == 1) {
        if(r == 0 && c == 0) {
            return 1;
        }
        if(r == 1 && c == 1) {
            return 2;
        }
        if(r == 1 && c == 0) {
            return 3;
        }
        if(r == 0 && c == 1) {
            return 4;
        }
    }

    int half = 1 << (n - 1);
    i64 b = 1LL << (2 * (n - 1));
    
    if (r < half && c < half) {
        return dfs1(n - 1, r, c);
    } else if (r >= half && c >= half) {
        return dfs1(n - 1, r - half, c - half) + 1 * b;
    } else if (r >= half && c < half) {
        return dfs1(n - 1, r - half, c) + 2 * b;
    } else {
        return dfs1(n - 1, r, c - half) + 3 * b;
    }
}

std::pair<int, int> dfs2(int n, i64 d) {
    if(n == 1) {
        if(d == 1) {
            return {0, 0};
        }
        if(d == 2) {
            return {1, 1};
        }
        if(d == 3) {
            return {1, 0};
        }
        if(d == 4) {
            return {0, 1};
        }
    }
    i64 b = 1LL << (2 * (n - 1));
    int q = (int)((d - 1) / b);
    i64 r = (d - 1) % b + 1;
    
    std::pair<int, int> pair = dfs2(n - 1, r);
    int half = 1 << (n - 1);
    
    if (q == 0) {
        return {pair.first, pair.second};
    } else if (q == 1) {
        return {pair.first + half, pair.second + half};
    } else if (q == 2) {
        return {pair.first + half, pair.second};
    } else {
        return {pair.first, pair.second + half};
    }
}

void solve() {
    int n, q;
    std::cin >> n >> q;
    while(q--){
        std::string op;
        std::cin >> op;
        if(op == "->"){
            int x, y;
            std::cin >> x >> y;
            int r = x - 1, c = y - 1;
            i64 ans = dfs1(n, r, c);
            std::cout << ans << "\n";
        }
        else if(op == "<-"){
            i64 d;
            std::cin >> d;
            std::pair<int, int> pair = dfs2(n, d);
            std::cout << pair.first + 1 << " " << pair.second + 1 << "\n";
        }
    }
}

int main(){
    int t;
    std::cin >> t;
    while(t--){
        solve();
    }
}

题目E:

Problem - E - Codeforces

题目大意:

首先定义MEX(a)为: 不在数组a中的最小非负整数

然后给你一个长度为n的数组a,现在你需要把这个数组分成k个连续子数组b1, b2,..bk

然后现在这k个数组生成k个数字MEX(bi), 现在你需要最大化k个数字中最小值,并输出这个最小值

解题思路:

设题目中的最小值为ans

首先可以发现,如果原数组a中没有0的话,无论怎么划分,这个ans肯定为0

然后数组长度为n ,   且包含的数字是0,1,..n - 1这n个整数,  

那么只划分一次的话,  ans就为MEX(a),结果为n

也就是理论上,答案的最小值为0,最大值为n

可以很容易的证明这是单调的,所以可以二分答案

现在问题变成了,每次的check函数应该写,改check什么比较好?

此时的mid为k个数组中的最小值,

我们只需要从头开始分割数组,从前面往后找

保证每一个数组的MEX都为k,

然后看能分出多少个数组,如果分出来的数组数量小于k那么就无法满足

那么问题现在问题,我们怎么快速算出从头开始划分数组,并求出每个数组的MEX呢?

可以用unordered_set但是会被hack,用set会超时

我们可以用一个长度为mid的used数组,定义个变量curr表示当前是第几个数组

每次遇到新的数字val,就放让used[val] = curr,这样就能很好的算出MEX了

具体细节操作可以看代码

代码(C++):

void solve() {
    int n, k;
    std::cin >> n >> k;

    std::vector<int> a(n), b(n + 1, 0);

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

    auto check = [&] (int x) -> bool {
        int cnt = 0, idx = 0;

        std::vector<int> used(x, 0);

        int curr = 1;
        while (idx < n) {
            int cur2 = 0;
            while (idx < n && cur2 < x) {
                int val = a[idx];
                if (val < x && used[val] != curr) {
                    used[val] = curr;
                    cur2++;
                }
                idx++;
            }

            if (cur2 < x) {
                break;
            }
            
            curr++;
            cnt++;
            if (cnt >= k) {
                return true;
            }
        }

        return false;
    };


    int high = 0;
    while (high <= n && b[high] > 0) {
        high++;
    }

    int low = 0;

    while (low < high) {
        int mid = (low + high + 1) / 2;
        if (check(mid)) {
            low = mid;
        } else {
            high = mid - 1;
        }
    }

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值