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];
posted @ 2024-07-10 23:30  03Goose  阅读(52)  评论(0)    收藏  举报
📑 文章目录