VP Codeforces Round 1025 (Div. 2)
A. It's Time To Duel
题意:第\(i\)个人和第\(i+1\)个人会比赛,每次比赛有一个人赢。给出一个\(01\)序列,\(s_i = 1\)表示第\(i\)个人至少赢了一次,\(s_i = 0\)表示一次也没赢。你需要判断这个序列是不是合法的。
有两个连续的\(0\)或者全是\(1\)就是错的。因为两个人之间至少有一个人赢,所以不可能有两个连续的\(0\)。
点击查看代码
void solve() {
int n;
std::cin >> n;
std::vector<int> a(n);
for (int i = 0; i < n; ++ i) {
std::cin >> a[i];
}
for (int i = 0; i + 1 < n; ++ i) {
if (a[i] == 0 && a[i + 1] == 0) {
std::cout << "YES\n";
return;
}
}
if (std::ranges::count(a, 1) == n) {
std::cout << "YES\n";
return;
}
std::cout << "NO\n";
}
B. Slice to Survive
题意:\(n\times m\)的矩阵,给出怪物的位置,两个人博弈。第一个人每次把矩阵横着切或者竖着切,保留怪物所在的那一个部分,第二个人每次可以任意更改怪物的位置。第一个人想尽快结束游戏,第二个人想慢点结束游戏。求游戏回合。
第二个人肯定是把怪物放在行列的中间,这样每次切掉的部分是最少的。那么如果矩阵大小是\(a\times b\),答案就是\(log_2a + log_2b\),但考虑开始时怪物的位置无法由第二个人决定,所以第一个人应该选择最少的切法。
点击查看代码
void solve() {
int n, m, a, b;
std::cin >> n >> m >> a >> b;
auto get = [&](int x) -> int {
int res = std::__lg(x);
if ((1 << res) < x) {
++ res;
}
return res;
};
int ans = std::min({get(a) + get(m), get(n - a + 1) + get(m), get(b) + get(n), get(m - b + 1) + get(n)});
std::cout << ans + 1 << "\n";
}
C1. && C2.
题意:交互题。一开始有一个\(x\),你并不知道。给你一个\(n\),你需要把\(x\)变成\(n\)。每次可以把\(x\)加上、乘上一个数,如果操作后\(x \notin [1, 1^{18}]\)则不会执行这场操作。或者把\(x\)除\(y\),如果\(y\)是\(x\)的因子才会执行操作,或者把\(x\)变成自己的数位和。\(C1\)可以操作\(7\)次,\(C2\)可以操作\(4\)次。
直接给出\(C2\)的做法,先乘\(9\),然后两次\(digit\),最后加上\(n-9\)就行。因为对于一个数\(n = d_k10^k + d_{k-1}10^{k-1}+...+d_010^{0}\),发现\(10^i \equiv 1 \pmod9\),那么\(n \equiv \sum_{i=0}^{k} d_i10^i \equiv \sum_{i=0} d_i \equiv digit(n) \pmod 9)\)。则如果\(n\)是\(9\)的倍数,那么\(digit\)也是\(9\)的倍数,那么我们乘上\(9\)后\(x\)变成了\(9\)的倍数,两次\(digit\)后变成了\(9\),那么加上\(n-9\)就是\(n\)了。
点击查看代码
int ask(const std::string & s, int y) {
std::cout << s << " " << y << std::endl;
int res;
std::cin >> res;
return res;
}
int digit() {
std::cout << "digit" << std::endl;
int res;
std::cin >> res;
return res;
}
void solve() {
int n;
std::cin >> n;
ask("mul", 9);
digit();
digit();
ask("add", n - 9);
std::cout << "!" << std::endl;
std::cin >> n;
std::cout.flush();
}
D. D/D/D
题意:给你一个图和一个多重集,对于每个点,你要在多重集里选一个数,假设它们的和为\(sum\),那么如果存在一条从\(1\)开始到\(i\)结束的长度为\(sum\)的路径\(i\)就是好的。求每个点是不是好的。
因为每个点可以经过多次,那么到达\(i\)后一直和相邻点反复横跳就行。那么我们求出到达\(i\)点最小奇数距离和最小偶数距离,看集合里最大的偶数和奇数有没有一个对应的大于最小距离就行了。于是就是一个\(dist[u][0/1]\)表示到达点\(u\)长度为奇数或偶数的最短距离。跑\(dijkstra\)就行。
点击查看代码
void solve() {
int n, m, k;
std::cin >> n >> m >> k;
std::vector<int> a(k);
for (int i = 0; i < k; ++ i) {
std::cin >> a[i];
}
std::vector<std::vector<int>> adj(n);
for (int i = 0; i < m; ++ i) {
int u, v;
std::cin >> u >> v;
-- u, -- v;
adj[u].push_back(v);
adj[v].push_back(u);
}
const int inf = 2e9 + 1;
std::vector dist(n, std::array<int, 2>{inf, inf});
using A = std::tuple<int, int, int>;
std::priority_queue<A, std::vector<A>, std::greater<A>> heap;
heap.emplace(0, 0, 0);
dist[0][0] = 0;
while (heap.size()) {
auto [d, t, u] = heap.top(); heap.pop();
if (d != dist[u][d & 1]) {
continue;
}
for (auto & v : adj[u]) {
if (dist[v][d + 1 & 1] > d + 1) {
dist[v][d + 1 & 1] = d + 1;
heap.emplace(d + 1, d + 1 & 1, v);
}
}
}
int sum = std::accumulate(a.begin(), a.end(), 0);
int max[2]{};
max[sum & 1] = sum;
for (int i = 0; i < k; ++ i) {
max[sum - a[i] & 1] = std::max(max[sum - a[i] & 1], sum - a[i]);
}
std::string ans(n, '0');
for (int i = 0; i < n; ++ i) {
if (max[0] >= dist[i][0] || max[1] >= dist[i][1]) {
ans[i] = '1';
}
}
std::cout << ans << "\n";
}