Rayan Programming Contest 2024 - Selection (Codeforces Round 989, Div. 1 + Div. 2) 题解 (A~E)
A. King Keykhosrow's Mystery
上限是 \(lcm(a, b)\),直接枚举即可
#include <bits/stdc++.h>
void solve()
{
int a, b; std::cin >> a >> b;
int l = std::lcm(a, b);
for(int i = std::min(a, b); i <= l; i++)
{
if(i % a == i % b)
{
std::cout << i << "\n";
return;
}
}
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t; std::cin >> t;
while(t--) solve();
return 0;
}
B. Rakhsh's Revival
贪心,每次 \(0\) 的数量够 \(m\) 个了就往后跳 \(k\)
#include <bits/stdc++.h>
void solve()
{
int n, m, k; std::cin >> n >> m >> k;
std::string s; std::cin >> s;
int cnt = 0, ans = 0;
for(int i = 0; i < n; i++)
{
if(s[i] == '0') cnt++;
if(s[i] == '1') cnt = 0;
if(cnt == m)
{
i = std::min(n, i + k - 1);
cnt = 0;
ans++;
}
}
std::cout << ans << "\n";
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t; std::cin >> t;
while(t--) solve();
return 0;
}
C. Trapped in the Witch's Labyrinth
整个网络可以看成一个有向图,超出网络视为终点,按照给出的字母连反边,从终点开始搜索,路上经过的点代表着最后都会走出这张图,所以不能成为答案,标记一下。处理完这些之后,再处理问号,对于一个问号,如果它上下左右的点都被标记了,说明它最后也会走出去,也不能成为答案。把这些点抛去之后的点就是答案。
#include <bits/stdc++.h>
void solve()
{
int n, m; std::cin >> n >> m;
std::vector<std::string> s(n);
for(int i = 0; i < n; i++) std::cin >> s[i];
std::vector<std::vector<int>> adj(n * m + 1);
int ed = n * m;
for(int i = 0; i < n; i++)
{
for(int j = 0; j < m; j++)
{
int x = i + (s[i][j] == 'D') - (s[i][j] == 'U');
int y = j + (s[i][j] == 'R') - (s[i][j] == 'L');
if(x >= n || y >= m || x < 0 || y < 0) adj[ed].emplace_back(i * m + j);
else adj[x * m + y].emplace_back(i * m + j);
}
}
std::vector<int> vis(n * m);
int ans = n * m, st = n * m;
std::queue<int> q;
q.emplace(st);
while(!q.empty())
{
int x = q.front(); q.pop();
for(auto y : adj[x])
{
vis[y] = 1;
ans--;
q.emplace(y);
}
}
std::array<int, 4> dx{0, -1, 0, 1}, dy{-1, 0, 1, 0};
for(int i = 0; i < n; i++)
{
for(int j = 0; j < m; j++) if(s[i][j] == '?')
{
int cnt = 0;
for(int dir = 0; dir < 4; dir++)
{
int x = i + dx[dir], y = j + dy[dir];
if(x >= n || y >= m || x < 0 || y < 0 || vis[x * m + y]) cnt++;
}
if(cnt == 4) ans--;
}
}
std::cout << ans << "\n";
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t; std::cin >> t;
while(t--) solve();
return 0;
}
D. Darius' Wisdom
不妨倒着考虑每一位会是什么。可以先用三个 \(set\) 把数字的下标都存下来,然后倒着考虑,先放 \(2\),再放 \(1\),最后是 \(0\)。
对于一个位置,如果它应该是 \(2\),但是这里填的是 \(0\),那么我们需要两次交换,这也是交换次数最多的交换。所以极限情况就是构造形如 \(222...21000...0\) 的序列,这种情况我们的总交换次数也不会超过 \(n\),所以就这么交换就好。注意每次我们会选最前面的数交换,因为越小的数最后的位置肯定越靠前,所以早点让它去前面是不劣的。
#include <bits/stdc++.h>
void solve()
{
int n; std::cin >> n;
std::vector<int> a(n);
for(int i = 0; i < n; i++) std::cin >> a[i];
std::array<std::set<int>, 3> pos;
for(int i = 0; i < n; i++) pos[a[i]].emplace(i);
std::vector<std::pair<int, int>> ans;
auto modify = [&](int idx) -> void
{
if(!pos[2].empty())
{
if(a[idx] == 2) pos[2].erase(idx);
else if(a[idx] == 1)
{
int tmp = *pos[2].begin();
std::swap(a[idx], a[tmp]);
pos[2].erase(tmp);
pos[1].erase(idx);
pos[1].emplace(tmp);
ans.emplace_back(idx, tmp);
}
else
{
int tmp1 = *pos[1].begin(), tmp2 = *pos[2].begin();
std::swap(a[idx], a[tmp1]);
std::swap(a[idx], a[tmp2]);
pos[2].erase(tmp2);
pos[1].erase(tmp1);
pos[1].emplace(tmp2);
pos[0].erase(idx);
pos[0].emplace(tmp1);
ans.emplace_back(idx, tmp1);
ans.emplace_back(idx, tmp2);
}
}
else
{
if(a[idx] == 1) pos[1].erase(idx);
else
{
int tmp = *pos[1].begin();
std::swap(a[idx], a[tmp]);
pos[1].erase(tmp);
pos[0].erase(idx);
pos[0].emplace(tmp);
ans.emplace_back(idx, tmp);
}
}
};
for(int i = n - 1; i >= 0; i--)
{
if(pos[1].empty() && pos[2].empty()) break;
modify(i);
}
std::cout << ans.size() << "\n";
for(auto [x, y] : ans)
std::cout << x + 1 << " " << y + 1 << "\n";
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t; std::cin >> t;
while(t--) solve();
return 0;
}
E. Permutations Harmony
首先可以把 \(n!<k\) 的情况判掉。
然后注意到 \(k\) 如果是偶数是一定可以的,因为我们可以构造 \(\frac{k}{2}\) 对 \({(a_i,n+1-a_i)}\) 排列。
接下来考虑 \(k\) 是奇数的情况:
首先如果 \(k=1\) 或 \(k=n!-1\) 是不行的,因为没的补齐。
我们可以算出来每一列的和是多少,是 \(\frac{n(n+1)}{2} \cdot k \cdot \frac{1}{n}=\frac{(n+1)k}{2}\),所以 \(n\) 此时必须是奇数。实际上我们只需要考虑 \(k=3\),构造出来之后就变成了 \(k\) 是偶数的情况。
考虑 \(n=7\) 的情况,每一列的和最后是 \(12\),先构造 \(7,6,5,4,3,2,1\),剩下需要的和为 \(\{5,6,7,8,9,10,11\}\),注意到我们可以构造 \(\{4,3,2,1,7,6,5\}\),剩下的序列为 \(\{1,3,5,7,2,4,6\}\),刚好满足条件。对于其它 \(n\) 也是这样。
#include <bits/stdc++.h>
using i64 = long long;
void solve()
{
int n, k; std::cin >> n >> k;
i64 fac = 1;
for(int i = 1; i <= n && fac < k + 10; i++)
fac *= i;
if(fac < k)
{
std::cout << "NO\n";
return;
}
if(n == 1)
{
std::cout << "YES\n";
std::cout << "1\n";
return;
}
if(k % 2 == 0)
{
std::cout << "YES\n";
std::vector<int> a(n);
std::iota(a.begin(), a.end(), 1);
for(int i = 0; 2 * i < k; i++)
{
for(int j = 0; j < n; j++) std::cout << a[j] << " \n"[j == n - 1];
for(int j = 0; j < n; j++) std::cout << n + 1 - a[j] << " \n"[j == n - 1];
std::next_permutation(a.begin(), a.end());
}
return;
}
if(k == 1 || k == fac - 1)
{
std::cout << "NO\n";
return;
}
if((n + 1) % 2)
{
std::cout << "NO\n";
return;
}
std::cout << "YES\n";
std::vector<int> a(n);
for(int i = 0; i < n; i++) a[i] = i + 1;
std::set<std::vector<int>> vis;
for(int i = 0; i < n; i++) a[i] = (n + 1) * 3 / 2 - a[i];
std::ranges::reverse(a);
std::vector<int> b(n);
for(int i = a[0] - 1, j = 0; i; i--, j++) b[j] = i;
for(int i = n, j = a[0] - 1; j < n; i--, j++) b[j] = i;
vis.emplace(b);
for(int i = 0; i < n; i++) b[i] = a[i] - b[i];
vis.emplace(b);
for(int i = 0; i < n; i++) b[i] = i + 1;
std::ranges::reverse(b);
vis.emplace(b);
std::ranges::reverse(b);
auto ans = vis;
while(ans.size() < k)
{
std::vector<int> c(b);
for(int j = 0; j < n; j++) c[j] = n + 1 - c[j];
if(!vis.contains(b) && !vis.contains(c))
{
ans.emplace(b);
ans.emplace(c);
}
std::next_permutation(b.begin(), b.end());
}
for(auto p : ans) for(int i = 0; i < n; i++) std::cout << p[i] << " \n"[i == n - 1];
}
int main()
{
std::ios::sync_with_stdio(false);
std::cin.tie(nullptr);
int t; std::cin >> t;
while(t--) solve();
return 0;
}