2024ICPC网络赛第二场

nuo534202 2024-09-30 09:07:03 阅读 59

VP链接

Codeforces:Dashboard - The 2024 ICPC Asia EC Regionals Online Contest (II) - Codeforces

QOJ:The 2024 ICPC Asia East Continent Online Contest (II) - Dashboard - Contest - QOJ.ac

A. Gambling on Choosing Regionals

注意到对于每个队伍来说最坏情况是与所有最强的队一个赛站,那么该队伍的排名会最低,所以为了让自己队伍的排名尽可能地高,最优选择就是去容纳量最小的赛站。

对 c 进行读入的时候只需要记录最小的 c 的值 

minc_i

,同时利用 unordered_map 记录每一个队伍在学校内的排名,将每个学校前

minc_i

 支队伍放进一个数组 b 里,再将数组 b 从大到小排序。

输出答案的时候,从头到尾依次遍历每一支队伍

如果队伍在学校的名次小于等于 

minc_i

,该队伍的最优排名就是在数组 b 里的下标如果队伍在学校的名次大于 

minc_i

,相当于要把同学校的一支队伍移出这个赛站,再将这支队伍放进这个赛站,那么只需要利用二分找出 b 数组中第一个小于该队伍能力的队伍的位置 pos(题目保证每个队伍能力不同,不存在等于的情况),该队伍最终的排名就是 pos - 1(要将其学校前面即能力较强的队伍移出,所以排名要 -1)。

<code>#include <bits/stdc++.h>

using namespace std;

const int N = 1e5 + 10;

int n, k, c, minc = INT_MAX, idx = 0, b[N];

struct node {

int w, id, rnk = 0;

bool operator< (const node &x) const {

return x.w > w;

}

} a[N];

unordered_map<string, priority_queue<node>> mp;

inline int read() {

int x = 0, f = 1; char c = getchar();

while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); }

while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();

return x * f;

}

int main() {

n = read(), k = read();

for (int i = 1; i <= k; i++) c = read(), minc = min(minc, c);

for (int i = 1; i <= n; i++) {

a[i].w = read(), a[i].id = i;

string s; char c = getchar();

while (c < 'A' || c > 'Z') c = getchar();

while (c >= 'A' && c <= 'Z') s += c, c = getchar();

// 时间限制只有 1s,用 cin 怕 TLE

mp[s].push(a[i]);

}

for (auto& [x, q] : mp) {

int num = 0;

while (!q.empty()) {

node cur = q.top(); q.pop();

a[cur.id].rnk = ++num; // 记录排名

if (num <= minc) b[++idx] = a[cur.id].w;

}

}

sort(b + 1, b + idx + 1, greater<int>()); // 从大到小排序

for (int i = 1; i <= n; i++) {

int l = 1, r = idx + 1;

while (l < r) {

int mid = l + r >> 1;

if (b[mid] > a[i].w) l = mid + 1;

else r = mid;

}

if (a[i].rnk <= minc) printf("%d\n", l);

else printf("%d\n", l - 1);

}

return 0;

}

F. Tourist

签到题,按照题意模拟即可。

#include <bits/stdc++.h>

using namespace std;

#define int long long

int n, c, tot = 1500, ans = -1;

inline int read() {

int x = 0, f = 1; char c = getchar();

while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); }

while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();

return x * f;

}

signed main() {

n = read();

for (int i = 1; i <= n; i++) {

c = read(), tot += c;

if (ans == -1 && tot >= 4000) ans = i;

}

printf("%d\n", ans);

return 0;

}

G. Game

可以注意到平局其实是没有用的,所以 Alice 获胜的概率是 

p_0 = \frac{a_0}{a_0 + a_1}

,Bob 获胜的概率是 

p_1 = \frac{a_1}{a_0 + a_1}


证明:

假设

P(x,y,a,b)

 为 Alice、Bob 在分别有 x、y 个 chips 且每一局的获胜概率分别为 a、b 的条件下,Alice 最终获胜的概率;

P_A(x,y,a,b)

 为 Alice 在当前条件下先赢一局,Alice 最终获胜的概率;

P_B(x,y,a,b)

 为 Bob 在当前条件下先赢一局且 Alice 最终获胜的概率。

那么有,

P(x,y,a,b) = a \cdot P_A(x,y,a,b) + b \cdot P_B(x,y,a,b) + (1 - a - b) \cdot P(x,y,a,b)

移项后有,

(a + b) \cdot P(x,y,a,b) = a \cdot P_A(x,y,a,b) + b \cdot P_B(x,y,a,b)

最终结果就是,

P(x,y,a,b) = \frac{a}{a+b} \cdot P_A(x,y,a,b) + \frac{b}{a+b} \cdot P_B(x,y,a,b)

从这个式子可以得出,每一种情况下,Alice 最终获胜的概率不受平局的影响。


双方的游戏过程实际上是一个辗转相减的过程,可以利用辗转相除来加速。

x \ge y

时,由于 Alice 获胜的情况较多,为方便统计,可以将问题转换成 1 - (Bob 获胜的概率)当 

x < y

 时,可以让 Alice 先连赢 

\frac{y}{x}

 次转换成第一种情况。

<code>#include <bits/stdc++.h>

using namespace std;

#define int long long

const int mod = 998244353;

inline int read() {

int x = 0, f = 1; char c = getchar();

while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); }

while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();

return x * f;

}

int qpow(int x, int k) {

int res = 1LL;

while (k) {

if (k & 1) res = res * x % mod;

x = x * x % mod;

k >>= 1;

}

return res % mod;

}

int f(int x, int y, int a, int b) {

if (x == 0) return 0;

return qpow(a, y / x) * ((1 - f(y % x, x, b, a) + mod) % mod) % mod;

} // 辗转相除求答案

signed main() {

int t = read();

while (t--) {

int x = read(), y = read(), a0 = read(), a1 = read(), b = read();

int inv = qpow(a0 + a1, mod - 2);

int A = inv * a0 % mod, B = inv * a1 % mod;

// 利用费马小定理求 Alice、Bob 获胜概率的乘法逆元

printf("%lld\n", f(x, y, A, B));

}

return 0;

}

I. Strange Binary

所有数字都能进行二进制拆分,由十进制数转换为二进制数,但是可能会由于存在连续的 0 因而不符合题目的条件。

注意到 

2^{i - 1} = 2^i - 2^{i - 1}

,因此对于每一个二进制数,相邻两位如果是 0 1 的话,那么可以将其转换成 1 -1,这样就不会存在连续的 0 了。

但如果该二进制数末尾有两个及以上的连续的 0 的时候,即 n 为 4 的倍数 时,那么将无法进行转换使得其满足题目条件。

<code>#include <bits/stdc++.h>

using namespace std;

int n, a[32];

inline int read() {

int x = 0, f = 1; char c = getchar();

while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); }

while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();

return x * f;

}

signed main() {

int t = read();

while (t--) {

n = read();

if (n % 4 == 0) {

puts("NO");

continue;

}

for (int i = 0; i < 32; i++) a[i] = 0; // 初始化

int idx = -1;

while (n) {

a[++idx] = n & 1;

n >>= 1;

} // 二进制拆分

for (int i = 0; i < 32; i++) {

while (i + 1 < 32 && a[i] != 0 && a[i + 1] == 0) {

a[i] = -1, a[i + 1] = 1;

i++;

}

} // 去 0 操作

puts("YES");

for (int i = 0; i < 32; i++) {

printf("%d ", a[i]);

if ((i + 1) % 8 == 0) puts("");

} // 输出答案

}

return 0;

}

J. Stacking of Goods

贪心,考虑所有物品以什么顺序叠放是最优的。

假设有两个相邻的物品 1 和 2(1 在上,2 在下),其对应的重量、体积、压缩系数分别表示为,

w_1

 、

v_1

 、

c_1

 和 

w_2

 、

v_2

 、

c_2

,这两个物品上面的物品重量的和记为 W。两个物品要交换的条件就是,交换后所有物体的最终体积更小。所有物体的最终体积可以表示为,所有物体的总体积 - 每个物体的压缩系数 * 该物体上面所有物体的重量和。由于所有物体的总体积永远不变,所以要让 每个物体的压缩系数 * 该物体上面所有物体的重量和 尽可能大。

即只需要满足 

c_1 \times W + c_2 \times (W + w_1) < c_2 \times W + c_1 \times (W + w_2)

,那么就将物体 1 和 2 交换位置(无论这两个物体是否交换位置,对其他物体的压缩体积不会造成任何影响)。化简之后,这个式子就变成了 

c_2 \times w_1 < c_1 \times w_2

要特别注意,有可能会出现连续多个 

c_2 \times w_1 = c_1 \times w_2

 的情况,这种情况下要让重量大的物品放在上面,这样重量大的物品贡献就最多,压缩的体积就更大。

<code>#include <bits/stdc++.h>

using namespace std;

#define int long long

const int N = 1e5 + 10;

int n;

struct node { int w, v, c; } a[N];

inline int read() {

int x = 0, f = 1; char c = getchar();

while (c < '0' || c > '9') { if (c == '-') f = -1; c = getchar(); }

while (c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();

return x * f;

}

bool cmp(node x, node y) {

if (x.c * y.w == x.w * y.c) return x.w > y.w;

return x.c * y.w < x.w * y.c;

}

signed main() {

n = read();

for (int i = 1; i <= n; i++) a[i].w = read(), a[i].v = read(), a[i].c = read();

sort(a + 1, a + n + 1, cmp); // 排序

int W = 0, V = 0;

for (int i = 1; i <= n; i++) {

V += a[i].v - a[i].c * W;

W += a[i].w;

} // 记录答案

printf("%lld\n", V);

return 0;

}

L. 502 Bad Gateway

假设最优的操作是一直进行第二个操作直到数字小于等于 c 以后再进行第一个操作。对于第二个操作,选到小于等于 c 的数的概率是 

\frac{c}{T}

,那么期望的操作时间就是 

\frac{T}{c}

。对于第一个操作,由于取到每个数的概率是相等的,所以期望的操作此时是 

\frac{1}{c}\sum_{i = 1}^{c}i = \frac{1}{c} \times \frac{c(c + 1)}{2} = \frac{c + 1}{2}

。由于是从第 0 秒开始操作,所以最后的结果是 

\frac{T}{c} + \frac{c + 1}{2} - 1 = \frac{2T + c(c - 1)}{2c}

,当 

c = \ \left \lfloor 2T \right \rfloor

 或 

c = \ \left \lceil 2T \right \rceil

 的时候这个式子取得最小值。

<code>#include <bits/stdc++.h>

using namespace std;

#define int long long

int n, T;

int gcd(int a, int b) { return a == 0 ? b : gcd(b % a, a); }

signed main() {

ios::sync_with_stdio(false); cin.tie(0);

cin >> n;

while (n--) {

cin >> T;

int num1 = floor(sqrt(1.0 * 2 * T)), num2 = ceil(sqrt(1.0 * 2 * T));

int x1 = 2 * T + num1 * (num1 - 1), y1 = 2 * num1;

int x2 = 2 * T + num2 * (num2 - 1), y2 = 2 * num2;

int GCD;

if (x1 * y2 < x2 * y1) {

GCD = gcd(x1, y1);

x1 /= GCD, y1 /= GCD;

printf("%lld %lld\n", x1, y1);

} else {

GCD = gcd(x2, y2);

x2 /= GCD, y2 /= GCD;

printf("%lld %lld\n", x2, y2);

}

}

return 0;

}



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。