AtCoder Beginner Contest 064
A
从高到低输入三个数位 \(a, b, c\) ,询问十进制 \(abc\) 是否是 \(4\) 的倍数。
view
int a, b, c; std::cin >> a >> b >> c;
int n = a * 100 + b * 10 + c;
std::cout << (n % 4 == 0 ? "YES" : "NO") << "\n";
B
题意:
数轴上,给 \(N\) 个点,坐标为 \(a_i(1 \leq i \leq N)\) ,可以从坐标轴上任意一个坐标开始,任意一个坐标结束。
询问经过所有给定点最少需要走的距离。
题解:
显然从第一个点走到最后一个点不回头是最短距离。
view
int N; std::cin >> N;
std::vector<int> a(N + 1);
for (int i = 1; i <= N; i++) {
std::cin >> a[i];
}
std::cout << *max_element(a.begin() + 1, a.end()) - *min_element(a.begin() + 1, a.end()) << "\n";
考虑加点难度。如果只能选择一个起点,每次访问一个给定点要回到起点,询问最少需要走多少距离。
不难得到 \(f(x) = \sum_{i = 1}^{N} |x - a_i|\) ,是单谷函数,且是个性质比显然的函数(高中教科书常见函数)。
设定义域为 \(M\) 。
具有诱惑性地,三分可以处理单谷函数,似乎可以三分 \(x\) ,\(O(N)\) 计算 \(f(x)\) ,从而 \(O(N \log M)\) 得到答案。
但是,能三分的单谷函数要求峰值两边严格单调,所以不能三分 \(x\) 坐标。除非处理的是 \(f(x) = \sum_{i = 1}^{N} |x - i|\) 。
实际上如果依赖于 \(N\) ,只有 \(O(N^{2})\) 的做法可以实现。
当定义域不大,考虑依赖于 \(M\) 。则可以 \(O(M)\) 实现。
递推式为 \(f(x) = f(x - 1) + pre(x) - suf(x)\) ,可以 \(O(M)\) 递推。
\(pre(x), suf(x)\) 分别为前缀和后缀给定点的数量,可以 \(O(M)\) 预处理。
总时间复杂度为 \(O(M)\) 。
C
题意:
分数为 \(x\) ,每个区间对应一种颜色。
共八个区间:
- [0, 399]
- [400, 799]
- [800, 1199]
- [1200, 1599]
- [1600, 1999]
- [2000, 2399]
- [2400, 2799]
- [2800, 3199]
若 \(x \geq 3200\) ,\(x\) 可以是任意一种颜色。
给 \(N\) 个 \(x_i(1 \leq x_i \leq N)\) ,询问最少和最多有多少种颜色。
题解:
不难发现每个区间为 \([400 i, 400 (i + 1) - 1] \ s.t.\ i \geq 0\) 。
对每个分数 \(x\) ,定位 \(p = min(8, \lfloor \frac{x}{400} \rfloor)\) ,记录 \(cnt[p]++\) 。
\(\forall x \in [0, 7]\) ,若 \(cnt[i] > 0\) ,让计数器 \(v++\) 。则可以得到有多少种颜色。
显然最少颜色为 \(max(v, 1)\) 。因为至少一个输入,若 \(v > 0\) ,可以全部变为已有颜色,否则至少存在一种颜色。
最多颜色为 \(v + cnt[8]\) 。
view
int N; std::cin >> N;
static int v[9];
for (int i = 1; i <= N; i++) {
int x; std::cin >> x;
int p = std::min(x / 400, 8);
v[p]++;
}
int cnt = 0; for (int i = 0; i < 8; i++) if (v[i]) ++cnt;
int mi = std::max<int>(cnt, 1);
int mx = cnt + v[8];
std::cout << mi << " " << mx << "\n";
如果增加难度:其实可以考虑变色只能选择 \(8\) 种颜色的一种改变,由鸽巢原理最多颜色会为 \(min(8, v + cnt[8])\) 。
D
题意:
给一个长度为 \(n\) 的括号序列 \(s\) 。可以在 \(s\) 中插入一些括号。
询问最短的且字典序最小的合法括号序列。
- 空序列是合法括号序列
- 若 x 是括号序列, (x) 是括号序列
- 若 x 、 y 是括号序列, x + y 是括号序列
题解:
考虑合法括号序列的折线图,设左括号 ‘(’ 为上升折线,右括号 ‘)’ 为下降折线。
设 \(d_i\) 为当前折线图的高度。
一个合法括号序列满足:
给定一个不合法括号序列,设 \(p_1, p_2\) (一定存在)分别是第一个和最后一个 \(d_i < 0\) 的位置。
于是只需要线性找到 \(x = -min(d_i)\) 。在 \(p_1\) 之前的任意位置增加 \(x\) 个'(' , \(p_2\) 之后的任意位置增加 \(d_{|s|} + x\) 个 ')' 就可以得到最短长度的合法括号序列。
而字典序最小只需让 '(' 添加到最左,')' 添加到最右。
view
int n; std::string s;
std::cin >> n >> s; s = " " + s;
std::vector<int> d(n + 1);
int x = 0;
for(int i = 1; i <= n; i++){
d[i] = d[i - 1] + (s[i] == '(' ? 1 : -1);
x = std::min<int>(x, d[i]);
}
x = -x;
std::cout << std::string(x, '(');
std::cout << s.substr(1, n);
std::cout << std::string(d[n] + x, ')');
浙公网安备 33010602011771号