在学长的提示下成功做出 D,蓝了,开心。
A. Hard Way
给定一个在第一象限的三角形座标,你可以从 $y=0$ 的任意位置出发,在平面中沿着折线前进但不能穿过三角形。
问三角形的边界上无法到达的位置,其总长度是多少。
分析
题面真 TM 难读,什么玩意。
注意到如果这条边是斜的,那么我们总是可以走一条更斜的线直接到达。如果此边水平,则不能到达。
即输出三角形水平边的长度。
using pii = pair<int, int>;
int main() {
int T;
cin >> T;
while (T--) {
vector<pii> v(3);
for (auto &[x, y] : v) {
cin >> x >> y;
}
sort(v.begin(), v.end(), [](pii a, pii b) {
if (a.second == b.second)
return a.first < b.first;
return a.second > b.second;
});
if (v[0].second == v[1].second)
cout << abs(v[0].first - v[1].first) << "\n";
else
cout << "0\n";
}
return 0;
}
B. Power Walking
有一些能量包,每个小孩的能力增幅是其得到不同种类能量包的个数。
总共有 $n$ 个能量包,其类别分别是 $\{a_i\}$。假如把这些分给 $k$ 个小孩,每个小孩至少得到一个能量包。
求对所有的 $0 \leqslant k \leqslant n$,求这 $k$ 个小孩最小的能量增幅。
分析
设这 $n$ 个能量包中类别不同的共有 $m$ 个。
- 若 $k \leqslant m$,则至少有 $m$ 的增幅,多出来的 $n-m$ 个可以和相同类别塞给同一个小朋友,没有额外增幅。
- 若 $k > m$,则至少有 $k$ 的增幅,多出来的 $n-k$ 个仍可以和相同类别合并,没有额外增幅。
总之,最小的能量增幅是 $\max(k, m)$。
int main() {
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<int> a(n);
for (auto &ai : a)
cin >> ai;
int m = n;
sort(a.begin(), a.end());
for (int i = 1; i < n; i++)
m -= a[i] == a[i - 1];
for (int i = 1; i <= n; i++) {
cout << max(i, m) << " \n"[i == n];
}
}
return 0;
}
C. Great Sequence
给定 $n$ 个数字 $\{a_i\}$,和一个数字 $x$。如果存在 $a_j$ 满足 $a_j = x a_i$,那么我们可以把这两个数配对。每个数字只能被配对一次。
现在允许往序列 $\{a_i\}$ 中添加数字,问若要让序列中所有数字都被配对,最少添加几个数字。
分析
排序后,双指针配对即可,时间复杂度 $O(n)$。Map 大概也能过,时间复杂度 $O(n \log n)$。
int main() {
int T;
cin >> T;
while (T--) {
int n;
ll x;
cin >> n >> x;
vector<ll> a(n);
for (auto &ai : a)
cin >> ai;
sort(a.begin(), a.end());
vector<int> vis(n);
int l = 0, r = -1;
while (r < n - 1) {
r++;
while (a[l] * x < a[r] || (a[l] * x == a[r] && vis[l]))
l++;
if (a[l] * x == a[r])
vis[l] = vis[r] = true;
}
int ans = 0;
for (auto vi : vis)
ans += vi;
cout << n - ans << "\n";
}
return 0;
}
D. Repetitions Decoding
给定一个长为 $n$ 的序列 $\{a_i\}$,可以在任意位置插入相同的两个元素 $[c, c]$。
Olya 希望序列能够被分成一系列子序列,并且所有子序列左右重复。
分析
对于左边第一个数 $a_1$,找到右侧与之相同的第一个数 $a_r$,不断在 $a_r$ 右侧插入数字使得 $a[1, r - 1]$ 到 $a[r, 2r - 2]$ 相同。然后再从 $2r-1$ 开始继续操作,直到序列结尾。
若原序列存在数字是出现了奇数次,则一定无解。前述算法一定不能运行到结尾。
注意到当原序列每个数字出现偶数次一定有解。而前述算法每次一定会减少一个数,故一定能找到解。
using pii = pair<int, int>;
int main() {
int T;
cin >> T;
while (T--) {
int n;
cin >> n;
vector<int> v(n);
for (auto &vi : v)
cin >> vi;
bool flag = true;
vector<pii> ans;
vector<int> ans2;
int l = 0;
while (l < v.size()) {
int r = -1;
for (int i = l + 1; i < v.size(); i++) {
if (v[i] == v[l]) {
r = i;
break;
}
}
if (r == -1) {
flag = false;
break;
}
int len = 1;
while (l + len < r) {
v.insert(v.begin() + r + len, {v[l + len], v[l + len]});
ans.emplace_back(r + len, v[l + len]);
len++;
}
l = r + len;
ans2.push_back(len * 2);
}
if (!flag) {
cout << "-1\n";
} else {
cout << ans.size() << "\n";
for (auto [x, y] : ans) {
cout << x << " " << y << "\n";
}
cout << ans2.size() << "\n";
for (auto ai : ans2) {
cout << ai << " ";
}
cout << "\n";
}
}
return 0;
}