本文为Codeforces Round 1016 (Div. 3) A - E 详细题解,觉得有帮助或者写的不错可以点个攒
目录
题目A:
题目大意:
给你一个数字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:
题目大意:
定义一个数字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:
题目大意:
给你两个数字,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:
解题思路:
“模拟”题,贴个我的代码吧
代码(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:
题目大意:
首先定义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";
}