Codeforces Round 951 (Div.2)

传送门

A. Guess the Maximum

时间限制:1秒        空间限制:256MB        输入:标准输入        输出:标准输出

问题描述

Alice 和 Bob 想出了一个相当奇怪的游戏。他们有一个整数数组 a_{1}, a_{2}, \cdots, a_{n}。Alice 选择一个整数 k 并告诉 Bob,然后发生以下事情:

  • Bob 选择两个整数 i 和 j (1 \leq i \leq j \leq n),然后在整数 a_{i}, a_{i + 1}, \cdots, a_{j} 中找到最大值;
  • 如果得到的最大值严格大于 k,则爱丽丝获胜,否则鲍勃获胜。

帮助 Alice 找到她确保获胜的最大 k。

输入格式

每个测试包含多个测试用例。第一行包含一个整数 t (1 \leq t \leq 10^{4}),表示测试用例的数量。每个测试用例的描述如下。

每个测试用例的第一行包含一个整数 n (2 \leq n \leq 5 \times 10 ^{4}),表示数组的元素数量。

每个测试用例的第二行包含 n 个整数 a_{1}, a_{2}, \cdots, a_{n} (1 \leq a_{i} \leq 10^{9}),表示数组的元素。

保证所有测试用例中 n 的总和不超过 5 \times 10^{4}

输出格式

对于每个测试用例,输出一个整数 —— 确保 Alice 获胜的最大整数 k。

输入样例

6\\ 4\\ 2\hspace{0.5em} 4 \hspace{0.5em} 1 \hspace{0.5em} 7\\ 5\\ 1 \hspace{0.5em} 2 \hspace{0.5em} 3 \hspace{0.5em} 4 \hspace{0.5em} 5\\ 2\\ 1 \hspace{0.5em} 1\\ 3\\ 37 \hspace{0.5em} 8 \hspace{0.5em} 16\\ 5\\ 10 \hspace{0.5em} 10 \hspace{0.5em} 10 \hspace{0.5em} 10 \hspace{0.5em} 9\\ 10\\ 3 \hspace{0.5em} 12 \hspace{0.5em} 9 \hspace{0.5em} 5 \hspace{0.5em} 2 \hspace{0.5em} 3 \hspace{0.5em} 2 \hspace{0.5em} 9 \hspace{0.5em} 8 \hspace{0.5em} 2

输出样例

3\\ 1\\ 0\\ 15\\ 9\\ 2\\

注释

在第一个测试用例中,Bob 可以选择的所有子段如下:[2,4],[2,4,1],[2,4,1,7],[4,1],[4,1,7],[1,7]。这些子段的最大值分别是 4,4,7,4,7,7。可以证明,3 是最大的整数,使得任何一个子段的最大值都将严格大于它。

在第三个测试用例中,Bob 唯一可以选择的子段是 [1,1]。所以答案是 0。

思路

如果任意子段的最大值都要大于 k,只需要保证所有长度为 2 的子段的最大值大于 k 即可。因此,k = min_{i = 0}^{n - 1}(max(a[i], a[i + 1]) - 1)

代码

#include <bits/stdc++.h>
using namespace std;
const int N = 5e4 + 10;
int t, n, 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;
}
int main() {
    t = read();
    while (t--) {
        int ans = INT_MAX;
        n = read(); for (int i = 1; i <= n; i++) a[i] = read();
        for (int i = 1; i < n; i++) {
            ans = min(ans, max(a[i], a[i + 1]) - 1);
        }
        printf("%d\n", ans);
    }
    return 0;
}

B. XOR Sequences

时间限制:1秒        空间限制:256MB        输入:标准输入        输出:标准输出

问题描述

给定两个不同的非负整数 x 和 y。考虑两个无限序列 a_{1}, a_{2}, a_{3}, \cdotsb_{1}, b_{2}, b_{3}, \cdots,其中

a_{n} = n \oplus x\\ b_{n} = n \oplus y

这里,x \oplus y 表示整数 x 和 y 的按位异或操作。

例如,若 x = 6,则序列 a 的前 8 个元素如下:[7, 4, 5, 2, 3, 0, 1, 14, …]。注意,元素的索引从 1 开始。

你的任务是找到序列 a 和 b 的最长公共子段的长度。换句话说,找到最大的整数 m,使得存在某些 i, j \geq 1,满足 a_{i} = b_{j}, a_{i +1} = b_{j + 1}, \cdots, a_{i + m - 1} = b_{j + m - 1}

注:序列 p 的一个子段是序列 p_{l}, p_{l + 1}, \cdots, p_{r},其中 1 ≤ l ≤ r。

输入格式

每个测试包含多个测试用例。第一行包含一个整数 t (1 \leq t \leq 10^{4}),表示测试用例的数量。接下来是每个测试用例的描述。

每个测试用例的唯一一行包含两个整数 x 和 y (0 \leq x, y \leq 10^{9}, x \not= y),表示序列的参数。

输出格式

对于每个测试用例,输出一个表示最长公共子段长度的整数。

输入样例

4\\ 0 \hspace{0.5em} 1\\ 12 \hspace{0.5em} 4\\ 57 \hspace{0.5em} 37\\ 316560849 \hspace{0.5em} 14570961\\

输出样例

1\\ 8\\ 4\\ 33554432

注释

在第一个测试用例中,序列 a 和 b 的前 7 个元素如下:

a = [1,2,3,4,5,6,7,…]
b = [0,3,2,5,4,7,6,…]

可以证明,不存在正整数 k,使得序列 [k,k + 1] 出现在 b 中作为一个子段。因此答案是 1。

在第三个测试用例中,序列 a 和 b 的前 20 个元素如下:

a = [56,59,58,61,60,63,62,49,48,51,50,53,52,55,54,41,40,43,42,45,…]
b = [36,39,38,33,32,35,34,45,44,47,46,41,40,43,42,53,52,55,54,49,…]

可以证明,最长公共子段之一是子段 [41,40,43,42],长度为 4。

思路

假设给定的 x, y 可以找到对应的 n, m 满足 x \oplus n = y \oplus m,如果 n, m 在自增之后依然满足这个条件,那么 n, m 每一个对应的二进制位必须同时改变或同时不变

对于这个条件,我们可以找到一组特殊的解,x = n, y = m,这样使得等式的异或值为 0。然后,我们将,n, m 相同的二进制位全部变为 0,这样就得到了一组最小的解。这个时候,n, m 能够自增的次数是最多的,公共子序列也是最长的。

要使得自增的时候等式成立,那么只有 n, m 对应的二进制数末尾连续的 0 能够被改变。假设 n, m 末尾分别有 t_{1}, t_{2} 个 0,那么答案就是 2^{min(t_{1}, t_{2})}

又因为 x \oplus n = y \oplus m \Rightarrow n \oplus m = x \oplus y,令 z = x \oplus y,我们要求的结果其实就是

z & (-z)

代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
int t;
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() {
    t = read();
    while (t--) {
        int x = read(), y = read(), z = x ^ y, ans = 0;
        printf("%lld\n", z & (-z));
    }
    return 0;
}

C. Earning on Bets

时间限制:2秒        空间限制:256MB        输入:标准输入        输出:标准输出

问题描述

你被邀请参加一个游戏。在这个游戏中,有 n 种可能的结果,对于每一种结果,你必须下注一定数量的整数硬币。如果第 i 种结果被判定为获胜,你将获得与你在该结果上的下注金额相同的硬币数,乘以 k_{i}。注意,只有一种结果会是获胜的。

你的任务是确定如何分配硬币,以便在任何获胜结果的情况下,你都能赢得比你下注的硬币总数多的硬币。更正式地说,你在所有可能的获胜结果上下注的硬币总数必须严格小于每种可能的获胜结果所收到的硬币数。

输入格式

每个测试包含多个测试用例。第一行包含一个整数 t (1 \leq t \leq 10^{4}),表示测试用例的数量。接下来是每个测试用例的描述。

每个测试用例的第一行包含一个整数 n (1 \leq n \leq 50),表示对应结果硬币的数量。

每个测试用例的第二行包含 n 个整数 kk_{1}, k_{2}, \cdots, k_{n} (2 \leq k_{i} \leq 20),表示如果第 i 种结果被判定为获胜,则硬币数量的倍数。

保证所有测试用例中 n 的总和不超过 2 \times 10^{5}

输出格式

对于每个测试用例,如果无法按要求分配硬币,则输出 -1。否则,输出 n 个整数 x_{1}, x_{2}, \cdots, x_{n} (1 \leq x_{i} \leq 10^{9}),表示你在每种结果上的下注。

可以证明,如果存在一个解,则总是存在一个满足这些约束条件的解。

如果存在多个合适的解,可以输出其中任意一个。

输入样例

6\\ 3\\ 3 \hspace{0.5em} 2 \hspace{0.5em} 7\\ 2\\ 3 \hspace{0.5em} 3\\ 5\\ 5 \hspace{0.5em} 5 \hspace{0.5em} 5 \hspace{0.5em} 5 \hspace{0.5em} 5\\ 6\\ 7 \hspace{0.5em} 9 \hspace{0.5em} 3 \hspace{0.5em} 17 \hspace{0.5em} 9 \hspace{0.5em} 13\\ 3\\ 6 \hspace{0.5em} 3 \hspace{0.5em} 2\\ 5\\ 9 \hspace{0.5em} 4 \hspace{0.5em} 6 \hspace{0.5em} 8 \hspace{0.5em} 3

输出样例

27 \hspace{0.5em} 41 \hspace{0.5em} 12\\ 1 \hspace{0.5em} 1\\ -1\\ 1989 \hspace{0.5em} 1547 \hspace{0.5em} 4641 \hspace{0.5em} 819 \hspace{0.5em} 1547 \hspace{0.5em} 1071\\ -1\\ 8 \hspace{0.5em} 18 \hspace{0.5em} 12 \hspace{0.5em} 9 \hspace{0.5em} 24\\

注释

在第一个测试用例中,硬币可以分配如下:27 枚硬币下注在第一种结果上,41 枚硬币下注在第二种结果上,12 枚硬币下注在第三种结果上。因此,在所有结果上下注的总硬币数量是 27+41+12=80 枚。如果第一种结果被判定为获胜,你将获得回报 3×27=81 枚硬币;如果第二种结果被判定为获胜,你将获得回报 2×41=82 枚硬币;如果第三种结果被判定为获胜,你将获得回报 7×12=84 枚硬币。所有这些值都严格大于 80。

在第二个测试用例中,一种方式是在每一种结果上下注一枚硬币。

思路

我们很容易发现获胜结束后收回的硬币数一定是 k_{i} 的倍数,而本题是一道构造题,所以我们不妨设最后收回的硬币数是所有 k 的最小公倍数 sum = lcm(k_{1}, k_{2}, \cdots, k_{n}),然后下注的硬币数x_{i} = \frac{sum}{k_{i}}。最后再统计下注的硬币数 total = \sum x_{i},比较 total 跟 sum 之间的大小关系。

  • 选择最小公倍数 lcm 的原因:收回的硬币数一定是 k_{i} 的倍数,那么一定是 lcm 的倍数;若选择 lcm 能够满足题目要求,选择 lcm 的倍数并将 x 扩大相应的倍数一定能够满足题目要求。
  • 选择 x_{i} = \frac{sum}{k_{i}} 来构造的原因:
  1. 若 x_{i} > \frac{sum}{k_{i}}x_{i} 过大会使 total 增大,可能会使 total < sum 变成 total \ge sum
  2. 若 x_{i} < \frac{sum}{k_{i}},使得当第 i 种结果被判定为获胜时,sum减小,可能会使 total < sum 变成 total >= sum。

代码

#include<bits/stdc++.h>
using namespace std;
int t, n, k[55], x[55];
#define int long long
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 lcm(int a, int b) { return a * b / (__gcd(a, b)); }
signed main() {
	t = read();
	while (t--) {
		n = read(); for (int i = 1; i <= n; i++) k[i] = read();
		int sum = 1, total = 0;
		for (int i = 1; i <= n; i++) {
			sum = lcm(sum, k[i]);
		}
		for (int i = 1; i <= n; i++) total += sum / k[i];
		if (total >= sum) puts("-1");
		else {
			for (int i = 1; i <= n; i++) printf("%d ", sum / k[i]); putchar('\n');
		}
	}
    return 0;
}
  • 32
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值