VP Educational Codeforces Round 16
A. King Moves
点击查看代码
void solve() {
std::string s;
std::cin >> s;
if ((s[0] == 'a' || s[0] == 'h') && (s[1] == '1' || s[1] == '8')) {
std::cout << 3 << "\n";
} else if ((s[0] == 'a' || s[0] == 'h') || (s[1] == '1' || s[1] == '8')) {
std::cout << 5 << "\n";
} else {
std::cout << 8 << "\n";
}
}
B. Optimal Point on a Line
中位数定理板子题。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
std::sort(a.begin(), a.end());
std::cout << a[n / 2 - (n % 2 == 0)] << "\n";
}
C. Magic Odd Square
题意:构造一个\(n\times n\)的矩阵,其中\([1, n^2]\)之间的数都恰好用一次,使得每行每列每个对角线之和都为奇数。保证\(n\)是奇数。
手玩一下发现规律,如果我们把最中间的行和最中间的列都放奇数,就已经满足了,考虑剩下的奇数怎么放。发现我们相当于画了一个坐标轴,如果我们想在其它地方放一个奇数,那么需要对其它三个象限对应的地方也放一个奇数。然后一开始我们用了奇数个奇数,还剩下偶数个奇数,只需要剩下的奇数个数是\(4\)的倍数就行了。因为\(n\)是奇数,那么总共有\(\lceil \frac{n^2}{2} \rceil = \frac{n^2+1}{2}\)个奇数,用了\(2n - 1\)个,剩下\(\frac{n^2+1}{2} - \frac{4n - 2}{2} = \frac{n^2 - 4n + 3}{2} = \frac{(n-1)(n-3)}{2}\)个,那么这个数一定是\(4\)的倍数。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector ans(n, std::vector<int>(n));
int x = 1, y = 2;
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < n; ++ j) {
if (std::abs(i - n / 2) + std::abs(j - n / 2) <= n / 2) {
ans[i][j] = x;
x += 2;
} else {
ans[i][j] = y;
y += 2;
}
}
}
for (int i = 0; i < n; ++ i) {
for (int j = 0; j < n; ++ j) {
std::cout << ans[i][j] << " \n"[j == n - 1];
}
}
}
D. Two Arithmetic Progressions
题意:对于两个等差序列,求有多少在\([L, R]\)之间的数同时在这两个等差序列里出现过。
根号分治。
考虑两个暴力做法:
- 枚举一个等差数列,判断是否在另一个等差序列里出现。
- 如果找到一个最小的一个\(a_1k + b_1 = a_2l + b_2\),那么每次给两边同时加上\(lcm(a_1, a_2)\)等式也成立并且在两个等差序列里出现。那么我们知道这个出现规律是以\(lcm(a_1, a_2)\)为周期的,我们只需要枚举\(lcm\)次,就可以找到第一个,然后能计算剩下多少循环。
我们可以在\(max(a_1, a_2) \geq 1000\)时用第一个方法,否则用第二个方法。
点击查看代码
void solve() {
i64 a1, b1, a2, b2, L, R;
std::cin >> a1 >> b1 >> a2 >> b2 >> L >> R;
if (a1 < a2) {
std::swap(a1, a2);
std::swap(b1, b2);
}
if (a1 >= 1000) {
int ans = 0;
for (i64 k = std::max(0ll, (L - b1 + a1 - 1) / a1); k * a1 + b1 <= R; ++ k) {
if ((k * a1 + b1 - b2) % a2 == 0 && (k * a1 + b1 - b2) / a2 >= 0) {
++ ans;
}
}
std::cout << ans << "\n";
} else {
L = std::max({L, b1, b2});
i64 lcm = a1 / std::gcd(a1, a2) * a2;
for (i64 i = L; i <= std::min(L + lcm, R); ++ i) {
if ((i - b1) % a1 == 0 && (i - b2) % a2 == 0) {
std::cout << (R - i) / lcm + 1 << "\n";
return;
}
}
std::cout << 0 << "\n";
}
}
E. Generate a String
题意:一开始你有一个\(m = 0\),你要变成\(n\),每次可以使\(m = m + 1, m = m - 1\),花费\(x\),也可以使\(m = m \times 2\),花费\(y\)。求最小代价。
\(bfs\)搜索,注意一些剪枝。每个数只可能被入队两次。
点击查看代码
void solve() {
int n, x, y;
std::cin >> n >> x >> y;
std::vector<i64> d(2 * n + 1, 1e18);
std::queue<std::pair<i64, int>> q;
d[n] = 0;
q.push({0, n});
while (q.size()) {
auto [val, u] = q.front(); q.pop();
if (u == 0) {
continue;
}
if (val > d[u] || val >= d[0]) {
continue;
}
if (u % 2 == 0 && d[u / 2] > d[u] + y) {
d[u / 2] = d[u] + y;
q.push({d[u / 2], u / 2});
}
if (d[u - 1] > d[u] + x) {
d[u - 1] = d[u] + x;
q.push({d[u - 1], u - 1});
}
if (u + 1 <= 2 * n && d[u + 1] > d[u] + x) {
d[u + 1] = d[u] + x;
q.push({d[u + 1], u + 1});
}
}
std::cout << d[0] << "\n";
}