ICPC 2024 网络赛(II)
F. Tourist
题目大意
你的初始rating是1500,告诉你接下来n场比赛的分数变动,问你的rating第一次大于等于4000是第几场,不可能则输出-1
解题思路
按题意模拟即可
代码实现
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
i64 n, cnt = 1500;
std::cin >> n;
for (int i = 1; i <= n; i++) {
int x;
std::cin >> x;
cnt += x;
if (cnt >= 4000) {
std::cout << i << "\n";
return 0;
}
}
std::cout << -1 << "\n";
}
I. Strange Binary
题目大意
给定一个二进制变种,二进制前面的系数为[-1, 0, 1],要求用这个二进制来表示数字n,同时不能出现相邻两个系数都是0,如果可以表示输出YES并给出构造方案,否则输出NO
解题思路
对于\(a_{i + 1} = 0,a_i = 1\),可以把它转化为\(a_{i + 1} = 1,a_i = -1\),这样就消去了0,对所有的位置都进行这样的操作,最后检查是否还存在相邻的0即可
代码实现
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int tt;
std::cin >> tt;
while (tt--) {
int n, f = 1;
std::cin >> n;
std::vector<int> a(32);
for (int i = 0; i <= 31; i++) {
a[i] = n >> i & 1;
}
for (int i = 0; i < 31; i++) {
if (a[i] == 1 && a[i + 1] == 0) {
a[i + 1] = 1;
a[i] = -1;
}
}
for (int i = 0; i < 31; i++) {
if (a[i] == 0 && a[i + 1] == 0) {
f = 0;
}
}
if (f) {
std::cout << "YES\n";
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 8; j++) {
std::cout << a[i * 8 + j] << " \n"[j == 7];
}
}
} else {
std::cout << "NO\n";
}
}
}
J. Stacking of Goods
题目大意
给你n个物品的重量w,初始体积v,压缩率c。定义压缩规则为:如果一个物品上方的重量为W,那么它的体积会变成v-c*W,要求最小化所有物品的体积之和
解题思路
- 当i在j上方时贡献为 \(v_i - c_i * W + v_j - c_j * (W + w_i)\)
- 当j在i上方时贡献为 \(v_j - c_j * W + v_i - c_i * (W + w_j)\)
贡献越小越好,化简得\(c_iw_j\)和\(c_jw_i\),自定义比较规则即可
代码实现
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
i64 n, ans = 0, W = 0;
std::cin >> n;
std::vector<std::array<i64, 3>> wvc(n);
for (int i = 0; i < n; i++) {
std::cin >> wvc[i][0] >> wvc[i][1] >> wvc[i][2];
ans += wvc[i][1];
}
std::sort(wvc.begin(), wvc.end(), [&](auto a, auto b) { return a[2] * b[0] < b[2] * a[0]; });
for (auto [w, v, c] : wvc) {
ans -= W * c;
W += w;
}
std::cout << ans << "\n";
}
A. Gambling on Choosing Regionals
题目大意
给定n个队伍的能力值和所属的学校,并告知k场比赛中每所学校最多可以派遣多少个队伍,每个队伍最多只能参加2场比赛,求最坏情况下每个队伍能获得的最好成绩
解题思路
最坏的情况就是所有强队都去一个赛站了,最优决策就是去容量最小的赛站,按照能力值排序,从大到小扫描即可
代码实现
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int n, k, min = INT_MAX;
std::cin >> n >> k;
std::vector<int> c(k), ans(n);
for (int i = 0; i < k; i++) {
std::cin >> c[i];
min = std::min(min, c[i]);
}
std::vector<std::tuple<int, std::string, int>> wsid(n);
for (int i = 0; i < n; i++) {
int w;
std::string s;
std::cin >> w >> s;
wsid[i] = {w, s, i};
}
std::sort(wsid.begin(), wsid.end(), std::greater<>());
int rank = 0;
std::map<std::string, int> mp;
for (auto [w, s, id] : wsid) {
ans[id] = rank;
if (mp[s] != min) {
ans[id]++;
mp[s]++;
rank++;
}
}
for (int i = 0; i < n; i++) {
std::cout << ans[i] << "\n";
}
}
G. Game
题目大意
A和B进行游戏,初始分别有x、y个筹码,A获胜概率为a0/b,B获胜概率是a1/b,其余情况平局。败者减少胜者当前筹码数量,平局直接进入下一轮,手中没有筹码者会输,问A赢得游戏的概率
解题思路
平局是没有用的,因此可以直接认为双方获胜概率是a0和a1,双方游戏过程其实是辗转相减,用辗转相除加速即可
代码实现
#include <bits/stdc++.h>
using i64 = long long;
const int MOD = 998244353;
i64 ksm(i64 a, i64 n, i64 mod) {
i64 res = 1;
a = (a % mod + mod) % mod;
while (n) {
if (n & 1) {
res = (a * res) % mod;
}
a = (a * a) % mod;
n >>= 1;
}
return res;
}
i64 f(i64 x, i64 y, i64 a, i64 b, i64 mod) {
if (x == 0) {
return 0;
}
return ksm(a, y / x, mod) * ((1 - f(y % x, x, b, a, mod) + mod) % mod) % mod;
}
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int tt;
std::cin >> tt;
while (tt--) {
int x, y;
std::cin >> x >> y;
int a0, a1, b;
std::cin >> a0 >> a1 >> b;
int A = ksm(a0 + a1, MOD - 2, MOD) * a0 % MOD, B = ksm(a0 + a1, MOD - 2, MOD) * a1 % MOD;
std::cout << f(x, y, A, B, MOD) << "\n";
}
}
L. 502 Bad Gateway
题目大意
给定一个计数器,初始时间为\([1, T]\)中的随机一个数字,每一轮有两种选择
- 什么也不做,计时器时间自己减少1
- 重置时间为\([1, T]\)中的随机一个数字
问让计时器显示0的期望轮数最小是多少,用最简分数表示
解题思路
最优操作一定是一直进行第二个操作直到小于等于c,再进行第一个操作。第一部分概率为\(c/T\),期望为\(T/c\);第二部分的概率是相等的,都是\(1/c\),期望是\(\frac{1}{c} \sum_{i=1}^{c} i = \frac{c + 1}{2}\),由于是0开始,因此还要-1。化简得\(\frac{2T + c(c - 1)}{2c}\),忽略常数项得\(\frac{c}{2} + \frac{T}{c}\),是一个明显的对勾函数,最小值在\(c=\sqrt{2T}\)
代码实现
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int n;
std::cin >> n;
for (int i = 0; i < n; i++) {
i64 t;
std::cin >> t;
i64 c1 = sqrtl(2 * t), c2 = sqrtl(2 * t) + 1;
i64 fz1 = 2 * t + c1 * (c1 - 1), fm1 = 2 * c1, fz2 = 2 * t + c2 * (c2 - 1), fm2 = 2 * c2;
if (fz1 * fm2 < fz2 * fm1) {
i64 g = std::__gcd(fz1, fm1);
std::cout << fz1 / g << " " << fm1 / g << "\n";
} else {
i64 g = std::__gcd(fz2, fm2);
std::cout << fz2 / g << " " << fm2 / g << "\n";
}
}
}
E. Escape
题目大意
无向图上有k个会同步移动的机器人,机器人每次会随机选邻边并记录当前经过的边,如果来回走同一条边,可以撤销这两次记录,行走记录超过长度d就自毁,求玩家避开所有机器人的情况下点1到点n的最短距离并输出路径,不可能输出-1
解题思路
分层图最短路,bfs预处理出机器人和玩家到达所有节点的最短奇步数和偶步数,同时记录到达该节点的上一节点,如果有答案根据记录节点回溯即可
代码实现
#include <bits/stdc++.h>
using i64 = long long;
int main() {
std::ios::sync_with_stdio(false);
std::cin.tie(0);
std::cout.tie(0);
int tt;
std::cin >> tt;
while (tt--) {
int n, m, d;
std::cin >> n >> m >> d;
std::vector<std::vector<int>> g(n + 1, std::vector<int>());
for (int i = 0; i < m; i++) {
int u, v;
std::cin >> u >> v;
g[u].push_back(v);
g[v].push_back(u);
}
int k;
std::cin >> k;
std::vector<std::array<int, 2>> dist1(n + 1, {-1, -1});
std::queue<std::array<int, 2>> q;
for (int i = 0; i < k; i++) {
int s;
std::cin >> s;
q.push({s, 0});
dist1[s][0] = 0;
}
while (!q.empty()) {
auto [u, f] = q.front();
q.pop();
for (auto v : g[u]) {
if (dist1[v][f ^ 1] == -1 && dist1[u][f] + 1 <= d) {
dist1[v][f ^ 1] = dist1[u][f] + 1;
q.push({v, f ^ 1});
}
}
}
std::vector<std::array<int, 2>> dist2(n + 1, {-1, -1}), pre(n + 1);
q.push({1, 0});
dist2[1][0] = 0;
while (!q.empty()) {
auto [u, f] = q.front();
q.pop();
for (auto v : g[u]) {
if (dist2[v][f ^ 1] == -1 && (dist1[v][f ^ 1] == -1 || dist1[v][f ^ 1] > dist2[u][f] + 1)) {
dist2[v][f ^ 1] = dist2[u][f] + 1;
pre[v][f ^ 1] = u;
q.push({v, f ^ 1});
}
}
}
int dist = INT_MAX, flag = -1;
for (auto f : {0, 1}) {
if (dist2[n][f] != -1 && dist2[n][f] < dist) {
dist = dist2[n][f];
flag = f;
}
}
if (dist == INT_MAX) {
std::cout << -1 << "\n";
} else {
std::cout << dist << "\n";
std::vector<int> ans;
while (n != 1 || flag != 0) {
ans.push_back(n);
n = pre[n][flag];
flag ^= 1;
}
ans.push_back(1);
std::reverse(ans.begin(), ans.end());
for (auto x : ans) {
std::cout << x << " \n"[x == ans.back()];
}
}
}
}