AtCoder Beginner Contest 068
A
没新意的字符串拼接题。
B
题意
给定 \(n\) ,找到一个 \(1 \leq x \leq n\) 的 \(x\) ,满足 \(x\) 可以被 \(2\) 除最多次。
题解
只需保证 \(x\) 在二进制下左侧的 \(0\) 尽可能多,这样的数有很多个。
不妨选 \(x = 2^{\alpha}\) ,容易构造。
让 \(x = 1\) ,当 \(x << 1 \leq n\) ,反复左移。最后的 \(x\) 是一个答案。
时间复杂度 \(O(\log n)\) 。
加强:如果要找到所有 \(1 \leq x \leq n\) ,满足 \(x\) 可以被 \(2\) 除最多次。
核心思想依旧是保证 \(x\) 左侧的 \(0\) 尽可能多,从最低位 \(1\) 开始,左侧在不超过 \(n\) 的情况下任意填。
C
题意
给一个图,\(1\) 和 \(n\) 点不直接连边。询问是否 \(1\) 可达 \(n\) 通过两条边。
题解
问题等价于 \(1\) 和 \(n\) 是否同时为某个节点的儿子。
D
题意
对于某个长度为 \(N\) 的非负整数序列 \(a_1, a_2, \cdots, a_N\) 。这个序列将反复执行下述操作:
- 选择序列的最大值 \(x\) ,让 \(x\) 减少 \(N\) 。其他所有值增加 \(1\) 。
当且仅当 \(x \leq N - 1\) 时候立刻停止操作。可以证明操作是有限次。
给定一个 \(K\) 。
要求构造一个长度为 \(N\) 的非负整数序列 \(a_1, a_2, \cdots, a_N\) ,恰好可以执行 \(K\) 次操作。
限制条件为 \(2 \leq N \leq 50, 0 \leq a_i \leq 10^{16} + 1000\) 。
\(0 \leq K \leq 50 \times 10^{16}\)
题解
哪怕没有限制条件我也不是很会啊……
先给两个显然的证明。
- 可以证明操作不会无限进行,因为每次操作后序列之和会减少 \(1\) 。
- 也可以证明操作不会导致序列出现负数,因为每次操作除了最大值都会增加。
下面是官方思路。
我们可以构造一个正整数序列,然后进行逆操作 \(K\) 次:
- 最小值 \(+ (N - 1)\) 。
- 其他值 \(-1\) 。
得到的序列即一个满足条件的序列。
逆操作是容易考虑的,但如何构造?
不妨选择 \(N\) 为 \(50\) ,序列初始为 \(0, 1, 2, \cdots, 49\) 。
好处是保证每次操作后不会有数相同,且每次最小值也不同。
观察到:
- 第 \(i\) 次操作的最小值为第 \(i \bmod 50 + 1\) 位。
- 每 \(50\) 次操作,所有数增加 \(50 - 49 = 1\) 。
于是可以 \(O(N)\) 先计算 \(\lfloor \frac{K}{50} \rfloor\) 次操作后的结果,再执行 \(K \bmod 50\) 次暴力。
view
i64 K; std::cin >> K;
const int N = 50;
std::vector<i64> a(N + 1);
std::iota(a.begin() + 1, a.end(), 0);
for (int i = 1; i <= N; i++) {
a[i] += K / N;
}
for (int i = 1; i <= K % N; i++) {
a[i] += N;
for (int j = 1; j <= N; j++) if (j != i) {
a[j] -= 1;
}
}
std::cout << N << "\n";
for (int i = 1; i <= N; i++) std::cout << a[i] << " \n"[i == N];
浙公网安备 33010602011771号