SnackDown2021 Online Round1A题解

比赛链接

DANCEMOVES

题意

给你起点和终点,每次可以往前走两步或者往后走一步,问最少多少步能从起点走到终点

题解

分类讨论即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <bits/stdc++.h>
using namespace std;

int T, x, y;

int main() {
cin >> T;
while (T--) {
cin >> x >> y;
if (x >= y)
cout << x - y << endl;
else if ((y - x) % 2 == 0)
cout << (y - x) / 2 << endl;
else
cout << (y + 3 - x) / 2 << endl;
}
return 0;
}

MINLCM1

题意

给定两个数x和k,你需要找两个位于\([x, x*k]\)内的不同的数i,j,问i和j的LCM的最大值和最小值分别是什么

题解

最大值很简单,只要i和j取最大的两个数即可,因为相邻的数互质,不会有比最大的两个数乘起来更大的

最小值即为\(2x\),不妨设j较大,因为\(\frac j {GCD(i,j)} \ge 2\),因此LCM最小为2i,i取最小值x即可

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

LL T, x, k;

int main() {
cin >> T;
while (T--) {
cin >> x >> k;
cout << 2 * x << " " << x * k * (x * k - 1) << endl;
}
return 0;
}

RRR

题意

n个人进行循环赛,胜者2分,负者0分,不并列名次,问第k名最多得多少分

题解

第k名可以把比他排名低的人全赢了,而比他排名高的最多赢一半

若超过一半可以计算出前k名的积分总量不对

可以简单构造出一种赢一半的情况,即为每个人赢自身编号往后数的一半人

1
2
3
4
5
6
7
8
9
10
11
12
13
#include <bits/stdc++.h>
using namespace std;

int T, k, n;

int main() {
cin >> T;
while (T--) {
cin >> n >> k;
cout << 2 * (n - k + (k - 1) / 2) << endl;
}
return 0;
}

EQBEAUTY

题意

给定一个数组A,可以做任意次如下操作:给数组中一个数加一或减一

用最少的操作次数使其能分为两个极差相等的数组,问最小操作次数

题解

首先将数组排序

若有一个数组只有一个数,那另一个数组全部都相等,显然这种情况下只有一个数的那个数应当为最大的数或最小的数,而另一个数组均等于原数组(指去掉了一个数的A)的中位数

若两个数组均不止一个数,显然一个数组最小值为a[1],可以枚举其最大值a[i], 那么另一个数组最大值为a[n],最小值应为a[1]+a[n]-a[i], 二分查找前后距离这个数最近的数暴力修改即可,需要判断是否能覆盖整个数组

1
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
#include <bits/stdc++.h>
using namespace std;
using LL = long long;

const int N = 1e5 + 10;

int T, n;
int a[N];

int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> T;
while (T--) {
cin >> n;
for (int i = 1; i <= n; ++i) {
cin >> a[i];
}
sort(a + 1, a + n + 1);
if (n == 2) {
cout << 0 << endl;
continue;
}
else if (n == 3) {
cout << min(a[2] - a[1], a[3] - a[2]) << endl;
continue;
}
LL mn = 1e18;
for (int i = 2; i < n; ++i) {
int tar = a[n] + a[1] - a[i];
int pos = lower_bound(a + 2, a + n, tar) - a;
if (pos != n && (pos < i || pos == i + 1))
mn = min(mn, 0LL + a[pos] - tar);
if ((pos - 1 < i && pos != 2) || pos == i + 2)
mn = min(mn, 0LL + tar - a[pos - 1]);
}
LL sum = 0LL;
int tar = a[n / 2];
for (int i = 1; i < n; ++i) {
sum += abs(tar - a[i]);
}
mn = min(mn, sum);
sum = 0;
tar = a[n / 2 + 1];
for (int i = 2; i <= n; ++i) {
sum += abs(tar - a[i]);
}
mn = min(mn, sum);
cout << mn << endl;
}
return 0;
}

BINFLIP

题意

给定一个长度为n的01串,其中前k个字符均为1

你需要将其变为全0串

操作如下:

你的第i次操作可以翻转一个长度为\(2^{i-1}\)的子串

任何时候翻转的字符不能越界

问能否成功,若能输出任意一种操作序列

题解

k为0输出Yes无需操作,由奇偶性可得出k为其他偶数均不行

k为奇数的情况下,首先找出最少需要的操作次数cnt,显然cnt为\(log_2k+1\),下面构造一种用只用cnt次操作的方法

首先算出\(\frac {2^{cnt}-1-k} 2\),这些数是用来将0翻转为1的,这样将1翻转为0的数量才能等于k

为了避免下标越界,只使用前k个位置即可

具体为对于将1翻转为0的操作(除了最后一次)从1开始

而将0翻转为1的操作以及最后一次将1翻转为0的操作从\(k - 2 ^ {cnt - 1} + 1\)开始即可

可以证明从\(k - 2 ^ {cnt - 1} + 1\)开始的\(\frac {2^{cnt}-1-k} 2\)个位置均被翻转了三遍,其余位置翻转了一遍

PS: 这里有个小问题是我没有证明将1翻转为0的操作(除了最后一次)的结束点一定比0翻转为1的结束点更大(即代码中那句assert), 这个证明起来并不困难,就让读者自己想想吧

1
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
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
LL gcd(LL a, LL b) {
return b == 0 ? a : gcd(b, a % b);
}
const int INF = 0x3f3f3f3f;
const LL INFL = 0x3f3f3f3f3f3f3f3f;
const int mod = 1e9 + 7;
//-------------------end head--------------

const int N = 1e5 + 10;

LL T, k, n;

int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cin >> T;
while (T--) {
cin >> n >> k;
if (k == 0) {
cout << "YES" << endl << 0 << endl;
} else if (k % 2 == 0) {
cout << "NO" << endl;
} else if (k == 1) {
cout << "YES" << endl << 1 << endl << 1 << endl;
} else {
int lim = 1, cnt = 0;
while (lim < k)
lim *= 2, cnt++;
lim -= 1;
int delta = (lim - k) / 2;
int pos = 1, neg = k - (1 << (cnt - 1)) + 1;
cout << "YES" << endl << cnt << endl;
for (int i = 0; i < cnt - 1; ++i) {
if (delta >> i & 1) {
cout << neg << endl;
neg += 1 << i;
} else {
cout << pos << endl;
pos += 1 << i;
}
}
assert(pos >= neg);
cout << k - (1 << (cnt - 1)) + 1 << endl;
}
}
return 0;
}