AtCoder Beginner Contest 066

A

题意

给定三个数,输出最小的两数之和。

题解

\(a+b+c - max({a, b, c})\)

B

题意

一个字符串被叫做 \(even\) 的当前仅当它看是两个相同字符串的拼接。

现在给一个 \(even\) 的且由小写字母组成的字符串 \(S\) 。询问从 \(S\) 的末尾删除一个以上字符后,\(S\) 能得到的最长的 \(even\) 字符串的长度。

题解

首先答案串长度一定是偶数,朴素的思路是在 \(S\) 的末尾逐次删除两个字符然后检查。

对初始串 \(S\) 跑一遍 \(KMP\)

假设答案串为 \(s\) ,长度为 \(n\) 。一定有 \(nxt[n] = \frac{n}{2}\)

时间复杂度 \(O(n)\)

view
	std::string s; std::cin >> s;
	int n = s.size();
	s = " " + s;

	std::vector<int> nxt(n + 1);
	for (int i = 1, j = 0; i < n; i++) {
		while (j > 1 && s[j + 1] != s[i + 1]) {
			j = nxt[j];
		}
		if (s[j + 1] == s[i + 1]) {
			j++;
		}
		nxt[i + 1] = j;
	}

	for (int i = n - 2; i >= 0; i -= 2) {
		if (nxt[i] == i / 2) {
			std::cout << i << "\n";
			return;
		}
	}

C

题意

给一个长度为 \(n\) 的整数序列 \(a_1, a_2, a_3, \cdots, a_n\) 。需要对一个空数组 \(b\) 实现 \(n\) 次操作:
\(i\) 次操作为:

  • \(a_i\) append 到 \(b\) 尾部。
  • 反转 \(b\)

输出 \(n\) 次操作后的 \(b\) 数组。

\(1 \leq n \leq 2 \times 10^{5}, 0 \leq a_i \leq 10^{9}\)

题解

一个不难的模拟,肯定是首尾的交替插入。

细致讨论一下就能解决。

但怎么更快的解决这个问题?

注意最后一个数,\(a_n\) 一定是插入到 \(b\) 的左边。

倒序考虑:\(a_{n - 1}\) 一定是插入到 \(b\) 右边,\(a_{n - 2}\) 一定是插入到 \(b\) 左边……

可以归纳出:

  • \(i \equiv n (\bmod 2)\) 时,\(a_i\) 插入到 \(b\) 的左边。
  • \(i \not \equiv n (\bmod 2)\) 时,\(a_i\) 插入到 \(b\) 的右边。

不妨直接使用 \(dequeue\) (但它是缓存非常不友好的数据结构,必要时可以用两个栈优化)。

view
	int n; std::cin >> n;
	std::deque<int> dque;
	for (int i = 1; i <= n; i++) {
		int x; std::cin >> x;
		if (i % 2 == n % 2) dque.push_front(x);
		else dque.push_back(x);
	}
	std::vector<int> a(dque.begin(), dque.end());
	for (int i = 0; i < a.size(); i++) {
		std::cout << a[i] << " \n"[i == (int)a.size() - 1];
	}

D

题意

给一个长度为 \(n + 1\) 的序列 \(a_1, a_2, a_3, \cdots, a_{n + 1}\) 。 包含 \(1, 2, 3, \cdots, n\) 的所有整数。

\(k = 1, 2, 3, \cdots, n + 1\) ,回答长度为 \(k\) 的不同子序列的个数,答案 \(\bmod 10^{9} + 7\)

\(1 \leq n \leq 10^{5}\)

题解

如果给的是一个长度为 \(n\) 的排列,那么下标和值是双射的。长度为 \(k\) 的不同子序列个数是 \(\binom{n}{k}\)

题目给的序列构造为 \(a_1, a_2, \cdots, x, \cdots, x, \cdots, a_{n - 1}, a_{n}\) 。有且仅有 \(x\) 出现两次,其他数都出现一次,且 \(1 \leq a_i \leq n\)

考虑从 \(n + 1\) 个数中选出 \(k\) 个数,那么得到的子序列显然存在多算的情况。

如何细致讨论?从 \(x\) 的贡献入手。

  • 如果子序列包含两个 \(x\) ,那么是唯一的。
  • 如果子序列不包含 \(x\) ,那么是唯一的。
  • 如果子序列包含一个 \(x\) ,且包含任意在两个 \(x\) 中间的 \(a_i\) ,那么是唯一的。
  • 如果子序列包含一个 \(x\) ,且不包含任意在两个 \(x\) 中间的 \(a_i\) ,那么会被计算两次。

设两个 \(x\) 中间的所有数的集合为 \(\mathbb{A}\)

于是被会被重复计算的子序列只有 \(x + \mathbb{S}\) ,其中:\(|\mathbb{S}| = k - 1, \mathbb{S} \cap \mathbb{A} = \emptyset\)

多出的数等于:任选两个 \(x\) 之一,在两个 \(x\) 的两端选剩余的 \(k - 1\) 个数的方案数。

于是长度为 \(k\) 的不同子序列个数为 \(\binom{n + 1}{k} - \binom{n + 1 - |\mathbb{A}| - 2}{k - 1}\)

时间复杂度可以做到 \(O(n)\) (但不妨用 map)。

view
	int n; std::cin >> n;
	std::vector<int> a(n + 2);
	std::map<int, int> loc;

	int d = 0;

	for (int i = 1; i <= n + 1; i++) {
		std::cin >> a[i];
		if (loc.count(a[i]) == 0)
			loc[a[i]] = i;
		else
			d = i - loc[a[i]] + 1;
	}

	for (int i = 1; i <= n + 1; i++) {
		std::cout << (comb(n + 1, i) - comb(n + 1 - d, i - 1) + MOD) % MOD << "\n";
	}

注意易错点: \(\binom{n + 1}{k} - \binom{n + 1 - |\mathbb{A}| - 2}{k - 1}\) 要考虑负数可能。

posted @ 2024-07-02 02:30  03Goose  阅读(24)  评论(0)    收藏  举报