Codeforces Round 1016 (Div. 3) A-G 题解
Codeforces Round 1016 (Div. 3) (opens new window)
# A - Ideal Generator
# Code
点击查看
#include <bits/stdc++.h>
using namespace std;
int main() {
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int T; cin >> T;
while (T--) {
int x; cin >> x;
if (x % 2 == 1)
cout << "YES" << '\n';
else
cout << "NO" << '\n';
}
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# B - Expensive Number
# Solution
显然,变成 肯定是最小的答案,思考怎么变成 ,就是所有不为 的个数加上最后一个非 数字后 的个数
# Code
点击查看
#include <bits/stdc++.h>
using namespace std;
void solve() {
string s;
int ans = 0, flg = 1;
cin >> s;
reverse(s.begin(), s.end());
for (auto c : s) {
if (c == '0') {
ans += flg;
}
else {
ans += 1;
flg = 0;
}
}
cout << ans - 1 << '\n';
}
int main() {
freopen ("B.in", "r", stdin);
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int T; cin >> T;
while (T--) solve();
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# C - Simple Repetition
# Solution
- 当 时,直接判断即可
- 当 时
- 如果 那么需要构造出这个数字,然后判断是否为素数
- 如果 那么肯定必然不是素数,如果 , 的位数为 ,那么肯定能被 整除,如果 的位数为 那么肯定能被 整除,以此类推
# Code
点击查看
#include <bits/stdc++.h>
using namespace std;
bool is_prime(int n) {
if (n < 2) return false;
for (int i = 2; i * i <= n; i++) {
if (n % i == 0) return false;
}
return true;
}
void solve() {
int n, k; cin >> n >> k;
if (k == 1) {
cout << (is_prime(n) ? "YES" : "NO") << '\n';
return ;
}
if (n == 1) {
int x = 0;
for (int i = 1; i <= k; i++)
x = x * 10 + n;
cout << (is_prime(x) ? "YES" : "NO") << '\n';
return ;
}
cout << "NO" << '\n';
return ;
}
int main() {
freopen ("C.in", "r", stdin);
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int T; cin >> T;
while (T--) solve();
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# D - Skibidi Table
# Solution
递归好题
对于第一问,定义函数 表示当边长为 时, 位置的值,采用递归思维
当 时,说明在左上角,
当 时,说明在右下角,
当 时,说明在左下角,
当 时,说明在右上角,
对于第二问也是同理,定义 表示,变长为 时, 数字所在的位置
- 当 说明在左上角,,
- 当 ,说明在左下角,,
后面就不写了,同理
# Code
点击查看
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
ll F[61];
ll query_1 (ll x, ll y, ll n) {
if (n == 0) return 1;
ll L = 1ll << n, cnt = L * L / 4;
if (x <= L / 2 && y <= L / 2)
return query_1(x, y, n - 1);
if (x > L / 2 && y > L / 2)
return query_1(x - L / 2, y - L / 2, n - 1) + cnt;
if (x > L / 2 && y <= L / 2)
return query_1(x - L / 2, y, n - 1) + cnt * 2;
if (x <= L / 2 && y > L / 2)
return query_1(x, y - L / 2, n - 1) + cnt * 3;
assert(0);
}
pair<ll, ll> query_2 (ll d, ll n) {
if (n == 0) return {1, 1};
ll L = 1ll << n, cnt = L * L / 4;
if (d <= cnt) {
auto [x, y] = query_2(d, n - 1);
return {x, y};
}
if (d <= cnt * 2) {
auto [x, y] = query_2(d - cnt, n - 1);
return {x + L / 2, y + L / 2};
}
if (d <= cnt * 3) {
auto [x, y] = query_2(d - cnt * 2, n - 1);
return {x + L / 2, y};
}
if (d <= cnt * 4) {
auto [x, y] = query_2(d - cnt * 3, n - 1);
return {x, y + L / 2};
}
assert(0);
}
void solve() {
ll n; int q; cin >> n >> q;
while (q--) {
string op; cin >> op;
if (op == "->") {
ll x, y; cin >> x >> y;
cout << query_1(x, y, n) << '\n';
}
else {
ll d; cin >> d;
auto [x, y] = query_2(d, n);
cout << x << ' ' << y << '\n';
}
}
}
int main() {
freopen ("D.in", "r", stdin);
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
for (int i = 0; i < 61; i++) {
F[i] = (1LL << i);
}
int T; cin >> T;
while (T--) solve();
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
# E - Min Max MEX
# Question
给定一个长度为 当数组 ,和一个整数
现在你需要把数组 分成 段,使得所有段的 mex 的最小值最大
# Solution
二分答案 mid,然后去 check 是否能分出 mid 段
对于 check,记录如果从 这些数字都出现过一次,那就段数
# Code
点击查看
#include <bits/stdc++.h>
using namespace std;
void solve() {
int n, k; cin >> n >> k;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++)
cin >> a[i];
vector<int> b(n + 1, 0);
auto check = [&] (int m) {
if (m == 0) return true;
int cnt = 0, now = 0;
for (int i = 0; i < m; i++)
b[i] = 0;
for (int i = 1; i <= n; i++) {
if (a[i] < m && b[a[i]] == 0) {
b[a[i]] = 1;
now += 1;
}
if (now == m) {
cnt += 1;
now = 0;
for (int j = 0; j < m; j++)
b[j] = 0;
}
}
return cnt >= k;
};
int L = 0, R = n + 1;
while (L + 1 < R) {
int mid = (L + R) / 2;
if (check(mid)) {
L = mid;
}
else {
R = mid;
}
}
cout << L << '\n';
return ;
}
int main() {
freopen ("E.in", "r", stdin);
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int T; cin >> T;
while (T--) solve();
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# F - Hackers and Neural Networks
# Question
这个题的题意不是很清楚,读了好久
给定一个长度为 的字符串数组 , 是一个字符串
和一个 个长度为 的字符串数组 , 是一个字符串
现在 是一个为空的,长度为 的字符串数组,你需要经过一些操作把 变成 ,操作二选一:
- 选择一个 ,计算机会随机选出现在 中的一个空位 ,然后把 复制给
- 选择一个位置 ,令 为空串
需要求最小操作次数,无解则输出
# Solution
先考虑无解,对于每一个 ,必然存在一个 使得 ,若没有则无解
我们可以构造出一种构造方式,设一个字符串数组 和 的不一样的字符串数为
那么一种可行的方式是:先执行 次 操作,然后对于哪些和 不一样的位置,执行一次 操作,然后针对那个位置一样的 做一次 操作
这样的总次数为
所以只需要找到 miss 最小的 即可
# Code
点击查看
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
void solve() {
int n, m; cin >> n >> m;
vector<string> a(n + 1);
vector<vector<string>> b(m + 1, vector<string>(n + 1));
for (int i = 1; i <= n; i++) cin >> a[i];
for (int i = 1; i <= m; i++) {
for (int j = 1; j <= n; j++) {
cin >> b[i][j];
}
}
for (int i = 1; i <= n; i ++) {
int flg = 0;
for (int j = 1; j <= m; j++) {
if (a[i] == b[j][i]) {
flg = 1;
break;
}
}
if (flg == 0) { // 无解
cout << "-1\n";
return;
}
}
int min_miss = INF;
for (int j = 1; j <= m; j++) {
int miss = 0;
for (int i = 1; i <= n; i++) {
if (a[i] != b[j][i]) {
miss++;
}
}
min_miss = min(min_miss, miss);
}
cout << n + 2 * min_miss << '\n';
return ;
}
int main() {
freopen ("F.in", "r", stdin);
// ios::sync_with_stdio(0);
// cin.tie(0); cout.tie(0);
int T; cin >> T;
while (T--) solve();
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# G - Shorten the Array
# Question
给定一个长度为 的数组 和一个整数
现在需要你选出两个整数 ,满足 且 最小
# Solution
一个很典的题目
建立一颗字典树,字典树的 的最后一个节点记录下 的下标,然后维护一个数组 max_son[i] 表示字典树上以 为根节点的子树中的最大下标
然后我们定义查询函数,枚举右边的那个数 ,对于一个 ,我们需要查询在字典树中满足 的最大下标,
这是一个很经典的做法
枚举 的每一位,假设当前枚举到第 位,设 第 位为 , 的第 位为
- 若 ,那么需要 的这一位为 ,所以要在字典树上往 的异侧走
- 若 ,如果 的这一位为 ,那么就不需要看后面的位了,需要把当前记录的最大下标和与 异侧的儿子的 max_son 做比较,然后往 的同侧走
每次先把 加入字典树中,然后查询,就可以保证字典树中的 了
# Code
点击查看
#include <bits/stdc++.h>
using namespace std;
const int INF = 0x3f3f3f3f;
void solve() {
int n, k; cin >> n >> k;
vector<int> a(n + 1);
for (int i = 1; i <= n; i++)
cin >> a[i];
struct Node {
int x, max_son;
int ch[2];
Node() : x(0), max_son(0) {
ch[0] = ch[1] = -1;
}
};
int cnt = 0;
vector<Node> tree(n * 61);
auto insert = [&] (int x, int id) {
int cur = 0;
for (int i = 30; i >= 0; i--) {
int bit = (x >> i) & 1;
if (tree[cur].ch[bit] == -1) {
tree[cur].ch[bit] = ++cnt;
tree[cnt] = Node();
}
cur = tree[cur].ch[bit];
tree[cur].max_son = max(tree[cur].max_son, id);
}
tree[cur].x = id;
};
auto query = [&] (int x) {
int cur = 0, ret = 0;
for (int i = 30; i >= 0; i--) {
int bit_k = (k >> i) & 1, bit_x = (x >> i) & 1;
if (bit_k == 1) {
cur = tree[cur].ch[bit_x ^ 1];
}
else {
if (tree[cur].ch[bit_x ^ 1] != -1)
ret = max(ret, tree[tree[cur].ch[bit_x ^ 1]].max_son);
cur = tree[cur].ch[bit_x];
}
if (cur == -1) break;
}
if (cur != -1)
ret = max(ret, tree[cur].max_son);
// ret = max(ret, tree[cur].max_son);
return ret;
};
int ans = INF;
for (int i = 1; i <= n; i++) {
insert(a[i], i);
int ret = query(a[i]);
if (ret > 0) {
ans = min(ans, i - ret + 1);
}
}
cout << (ans == INF ? -1 : ans) << '\n';
}
int main() {
freopen ("G.in", "r", stdin);
freopen ("G.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0); cout.tie(0);
int T; cin >> T;
while (T--) solve();
return 0;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77