【A】Azulejos
题意简述:
有两排瓷砖,每排都有 \(n\) 个,每个瓷砖有高度 \(h_i\) 和价格 \(p_i\) 两种属性。
你需要分别重新排列这两排瓷砖,使得同一排的瓷砖满足价格不降,后一排的瓷砖的高度严格大于前一排对应瓷砖的高度。
判断无解或输出一种合法方案。
题解:
首先要满足价格不降,那么先把两排瓷分别按照 \(p_i\) 排序。
如果同一排中的两个瓷砖 \(p_i\) 不同,那么顺序已经确定。但是如果 \(p_i\) 相同,就可以任意交换顺序。
也就是说,前后两排中的瓷砖都被划分为了若干区间,每个区间中的瓷砖的 \(p_i\) 都相同,而且可以任意交换顺序。
让我们从两排的第一个区间开始考虑,这时有两种情况。
第一种是前一排的区间长度较短,那么此时前排的区间中的每个瓷砖都需要与后排的区间中的某个瓷砖配对。
那么在保证尽量能构造出解的前提下,最好把更多更优(\(h_i\) 尽量大)的后排的瓷砖留给后续考虑。
这引出一个贪心策略,使用 set
按照 \(h_i\) 为关键字维护瓷砖,
对于前排的每个瓷砖,在后排中寻找一个 \(h_i\) 尽量小,但是比当前瓷砖大的瓷砖与其配对,这样可以保证留给后面的瓷砖尽量优。
第二种是后一排的区间长度较短,相反地,我们对于后排的每个瓷砖寻找前排中 \(h_i\) 尽量大,但是比当前瓷砖小的瓷砖与其配对即可。
这种策略可以保证留给后续考虑的瓷砖尽量优。那么我们只需要对还未配对的瓷砖继续考虑即可。
不难写出代码,时间复杂度为 \(\mathcal{O}(n\log n)\):
#include <cstdio>
#include <algorithm>
#include <set>
const int MN = 500005;
int N, Ans1[MN], Ans2[MN];
struct dat{ int p, h, id; dat() {} dat(int h, int id) : h(h), id(id) {} } a1[MN], a2[MN];
inline bool operator <(dat i, dat j) { return i.h == j.h ? i.id < j.id : i.h < j.h; }
std::set<dat> s1, s2;
int main() {
scanf("%d", &N);
for (int i = 1; i <= N; ++i) scanf("%d", &a1[i].p);
for (int i = 1; i <= N; ++i) scanf("%d", &a1[i].h);
for (int i = 1; i <= N; ++i) scanf("%d", &a2[i].p);
for (int i = 1; i <= N; ++i) scanf("%d", &a2[i].h);
for (int i = 1; i <= N; ++i) a1[i].id = a2[i].id = i;
std::sort(a1 + 1, a1 + N + 1, [](dat i, dat j) { return i.p < j.p; });
std::sort(a2 + 1, a2 + N + 1, [](dat i, dat j) { return i.p < j.p; });
int cnt = 0;
for (int i = 0; i <= N; ++i) {
if (a1[i].p != a1[i + 1].p || a2[i].p != a2[i + 1].p) {
if (s1.size() < s2.size()) {
for (auto j : s1) {
auto it = s2.lower_bound(dat(j.h, 1));
if (it != s2.begin()) {
--it, ++cnt;
Ans1[cnt] = j.id;
Ans2[cnt] = it->id;
s2.erase(it);
}
else return puts("impossible"), 0;
}
s1.clear();
}
else {
for (auto j : s2) {
auto it = s1.upper_bound(dat(j.h, N));
if (it != s1.end()) {
++cnt;
Ans2[cnt] = j.id;
Ans1[cnt] = it->id;
s1.erase(it);
}
else return puts("impossible"), 0;
}
s2.clear();
}
if (a1[i].p != a1[i + 1].p)
for (int j = i + 1; j <= N && a1[j].p == a1[i + 1].p; ++j)
s1.insert(a1[j]);
if (a2[i].p != a2[i + 1].p)
for (int j = i + 1; j <= N && a2[j].p == a2[i + 1].p; ++j)
s2.insert(a2[j]);
}
}
for (int i = 1; i <= N; ++i) printf("%d ", Ans1[i]); puts("");
for (int i = 1; i <= N; ++i) printf("%d ", Ans2[i]); puts("");
return 0;
}
【B】Beautiful Bridges
待补
【C】Checks Post Facto
待补
【D】Circular DNA
【E】Dead-End Detector
【F】 Directing Rainfall
待补
【G】First of Her Name
待补
【H】Hobson's Trains
待补
【I】Karel the Robot
待补
【J】Miniature Golf
待补
【K】Traffic Blights
待补