2024牛客暑期多校训练营3
2024牛客暑期多校训练营3
L-Sudoku and Minesweeper_2024牛客暑期多校训练营3 (nowcoder.com)
题意
给一个 9×9 的填好的数独,要求保留其中一部分数字(至少保留 1 个),剩下的替换成”地雷”。
要求满足对于每个数字,与它 8 连通的格子中是地雷的数量恰好等于自身。
思路
选择一个不在边上的8
即可,其他地方全改为地雷。
代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
vector<string> s(9);
for (auto &i : s)
cin >> i;
for (int i = 0; i < 9; i ++) {
for (int j = 0; j < 9; j ++) {
if (i && j && i != 8 && j != 8 && s[i][j] == '8') {
for (auto &c : s)
c = string(9, '*');
s[i][j] = '8';
for (auto &c : s)
cout << c << '\n';
return 0;
}
}
}
return 0;
}
B-Crash Test_2024牛客暑期多校训练营3 (nowcoder.com)
题意
给定 n 类操作,每次可将 \(x\) 更新为 \(|x-a_i|\)
求从 D 开始可得到的最小值
思路
考虑只选择一类操作,则最终得到的答案即是 \(\min(D\%h_i,h_i-D\%h_i)\)。
根据数论中的裴蜀定理,即设 \(a,b\) 是不全为零的整数,对任意整数 \(x,y\) 满足 \(\gcd(a,b)|(ax+by)\),且存在整数 \(x,y\) 使得 \(ax+by=\gcd(a,b)\)。
假设对于 \(h_1,h_2\dots,h_n\) 用了 \(x_1,x_2\dots,x_n\) 次操作, 那么对于 \(h_1x_1+h_2x_2+\dots+h_nx_n\) 一定存在一个 \(x\) 序列使得其等于 \(\gcd(h_1,h_2\dots,h_n)\)。
所以最终答案就是 \(\min(D\%\gcd_h,\gcd_h-D\%\gcd_h)\)。
代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
i64 n, D;
cin >> n >> D;
i64 g = 0;
while (n--) {
i64 h;
cin >> h;
g = gcd(h, g);
}
cout << min(g - D % g, D % g) << '\n';
return 0;
}
A-Bridging the Gap 2_2024牛客暑期多校训练营3 (nowcoder.com)
题意
n 个人在河的一侧,第 i 个人有体力值 \(h_i\)
有且仅有一艘能容纳 L 到 R 个人的船
问是否能将所有人从河的左侧运送到右侧
思路
感觉题解讲得很好了
需要从河的右侧往回运送的趟数最小值:\(S=\lceil\frac{n-R}{R-L}\rceil\)
令 \(a_i=\lfloor\frac{h_i-1}{2}\rfloor\) 为第 i个人能多来回的趟数
一个必要条件是 \(\sum\limits_{i=1}^n\min(a_i,S)≥SL\)
下述贪心保证条件的充分性:每次将体力值最高的若干个人从一侧运输
到另一侧。
贪心成立性的原因在于每次从右侧往回运的过程相当于每次将
\(a_1, a_2, . . . , a_n\)中最大的 L个元素减一,更新后的数组 \(a'_i\) 满足 \(\sum\limits_{i=1}^n\min(a'_i,S-1)≥(S-1)L\) 。由数学归纳法可知正确性。
代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, l, r;
cin >> n >> l >> r;
i64 S = ceil(1.0 * (n - r) / (r - l));
i64 ans = 0;
while (n--) {
int h;
cin >> h;
ans += min(S, 1ll * (h - 1) / 2);
}
puts(ans >= S * l ? "Yes" : "No");
return 0;
}
J-Rigged Games_2024牛客暑期多校训练营3 (nowcoder.com)
题意
两队打比赛,大比分 2b − 1 赢,小比分 2a − 1 赢。
给定的长度为 n 的串,两队比赛的每个小分结果是这个串的循环重复。
问从该串的每个位置开始,最终谁会赢得整个比赛。
思路
倍增。
首先对于每个位置,计算出它 \(2a-1\) 局后的比分的比分终点的位置。
然后采用倍增,即假设我们要从 \(j\) 跳 \(2^i\) 步,可以先从 \(j\) 跳 \(2^{i-1}\) 步,然后从跳 \(2^{i-1}\) 的终点也就是 \(jump_{2^{i-1},j}\) 再跳 \(2^{i-1}\) 步,即 \(jump_{i,j} = jump_{i-1,jump_{i-1},j}\) 。
同理,从 \(j\) 跳 \(2^i\) 步的得分我们也要累计起来,但这个时候要统计两部分的 a 小局比分,即 \(j\sim j+2^{i-1}\) 和 \(j+2^{i-1}\sim j+2^i\) 的。
最终判断的时候每次从 \(2^{17}\) 往后跳到凑出 \(2b-1\) 的比分即可。(\(2^{17}>1e5\))
代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n, a, b;
cin >> n >> a >> b;
vector jump(18, vector<int>(n));
vector sum(18, vector<int>(n));
string s;
cin >> s;
int sc[2] {}, r = 0;
for (int i = 0; i < n; i ++) {
while (sc[1] < a && sc[0] < a) {
sc[s[r] - '0']++;
r = (r + 1) % n;
}
jump[0][i] = r;
sum[0][i] = sc[1] > sc[0];
sc[s[i] - '0']--;
}
for (int i = 1; i < 18; i ++) {
for (int j = 0; j < n; j ++) {
jump[i][j] = jump[i - 1][jump[i - 1][j]];
sum[i][j] = sum[i - 1][j] + sum[i - 1][jump[i - 1][j]];
}
}
int pos, res, ans;
for (int i = 0; i < n; i ++) {
pos = i, res = 0, ans = 0;
for (int j = 17; j >= 0; j --) {
if (res + (1 << j) <= 2 * b - 1) {
res += 1 << j;
ans += sum[j][pos];
pos = jump[j][pos];
}
}
cout << (ans >= b);
}
return 0;
}
D-Dominoes!_2024牛客暑期多校训练营3 (nowcoder.com)
题意
有 \(n\) 张骨牌,每个骨牌上写有 \(2\) 个数字 \(x_i, y_i\)。
要求将 \(n\) 张骨牌横着摆成一排,一共 \(2n\) 个数字,保证对于相邻的不属于
同一骨牌的数字互不相同。
思路
感觉有点模拟的意味。
对于每一对 \((x,y)[x\ne y]\),如果当前要放得 \(x\) 与前一次的 \(y\) 相同,那就调换顺序。
如果当前的 \(x=y\) ,那我们把这个结果存起来,碰到下一次又有类似的就计数一下或者两者分开放,就类似上一个结论,只不过是把 \((x,y)\) 抽象成了 \(((x_1,y_1),(x_2,y_2))[x_1=y_1,x_2=y_2]\)。
最后如果还存在多余的 \((x,y)[x=y]\),那就在之前已经确定好了的顺序中插空放。
注意判断最前面和最后面。
代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
int n;
cin >> n;
int has = 0, num = 0;
vector<int> a, b;
for (int i = 0; i < n; i ++) {
int x, y;
cin >> x >> y;
if (x != y) {
if (b.size() && b.back() == x) {
a.emplace_back(y);
b.emplace_back(x);
} else {
a.emplace_back(x);
b.emplace_back(y);
}
} else {
if (!num || has == x) {
has = x;
num ++;
} else {
if (!b.size() || b.back() == x) {
a.emplace_back(has);
b.emplace_back(has);
a.emplace_back(x);
b.emplace_back(x);
} else {
a.emplace_back(x);
b.emplace_back(x);
a.emplace_back(has);
b.emplace_back(has);
}
num --;
}
}
}
vector<pair<int, int>> ans;
for (int i = 0; i < a.size(); i ++) {
if (num && i && a[i] != b[i - 1] && a[i] != has && b[i - 1] != has) {
ans.emplace_back(has, has);
num --;
}
if (num && !i && a[i] != has) {
ans.emplace_back(has, has);
num --;
}
ans.emplace_back(a[i], b[i]);
}
if (num && (!b.size() || b.back() != has)) {
ans.emplace_back(has, has);
num --;
}
if (num) {
cout << "No\n";
} else {
cout << "Yes\n";
for (auto &[x, y] : ans)
cout << x << ' ' << y << '\n';
}
return 0;
}