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}\) 要考虑负数可能。
浙公网安备 33010602011771号