题解 实战教学(magic)
终于,凭借 \(upper\_bound\)、\(map\)、\(线段树\)的三重 \(\log n\),我是最劣解!!!
实战教学(magic)
题目背景
芙莉莲正在带着菲伦进行实战教学,她们将要讨伐封印松动的腐败贤者古瓦尔。
“好久不见啊,芙莉莲。过去多久了……”
“八十年。”
“魔王大人呢?”
“死了。”
“哈。那就让我来为他复仇吧。”
尽管芙莉莲有几百种方法秒杀这家伙,但考虑到村子就在旁边,她得选个动静小点的魔法。具体来说,一个魔法的威力定义为……
题目描述
给定两个长度为 \(2n\) 的序列 \(a,b\),你需要将 \(1\) 到 \(2n\) 两两配对,配成 \(n\) 对数 \((x_i,y_i)\),则该配对方案的威力为 \(\max\limits_{i=1}^n(\max(a_{x_i},a_{y_i})+\max(b_{x_i},b_{y_i}))\)。求所有配对方案的最小威力。
输入格式
第一行,一个正整数 \(n\)。
第二行,\(2n\) 个正整数 \(a_i\)。
第三行,\(2n\) 个正整数 \(b_i\)。
输出格式
共一行,一个正整数,表示最小威力。
输入样例1
2
740686581 631501894 873746401 700418216
752774347 926102532 84896384 164680887
输出样例1
1626520748
开始乱讲:
首先注意到这个奇奇怪怪的所求的东西,我们并没有什么很好的策略可以快速求出,(也许推推可以有一个O\((n^2)\)的 DP?)
不妨假设我们已经找到了那个最优的解法,那个解法找出的最优解是 \(lim\),则这个数字 \(lim\) 在序列中是一个“满足条件”的数字,不难发现若一个数字 \(lim\) 满足条件,则当然所有大于它的数字都满足条件,只是没有 \(lim\) 优秀而已,所以使用二分答案。
check函数的实现成了另一个问题,它当然是一个复杂度为 \(O(n \log n)\) 的算法啦!毕竟它给着那么小的数据范围和不和理的 \(2s\) 时间限制,\(O(n)\) 岂不是很没面子。
在这个函数里面我们考虑贪心,不妨建立两个数组 \(p、q\) ,分别按着 \(a\) 从大到小、\(b\) 从小到大的顺序排列。考虑以下例子。
| a | 1 | 4 | 9 | 2 |
|---|---|---|---|---|
| b | 6 | 3 | 2 | 10 |
| id | 1 | 2 | 3 | 4 |
(不用样例是因为太丑了)
则 \(p\) 数组是这样的
| a | 1 | 2 | 4 | 9 |
|---|---|---|---|---|
| b | 6 | 10 | 3 | 2 |
| id | 1 | 4 | 2 | 3 |
\(q\) 是这样的
| a | 10 | 4 | 1 | 2 |
|---|---|---|---|---|
| b | 2 | 3 | 6 | 10 |
| id | 3 | 2 | 1 | 4 |
考虑贪心策略,假设现在二分的 \(mid = 13\),从 \(p_{a1}\) 开始,我们可以得到它所能接受的最大的 \(b = mid - p_{a1} = 7\),可以二分找到一个最大的令 \(y\) 满足的 \(q_r\)(在这里用q的下标代表是3), 我们再找出 \(q_1...q_r\) 中 \(q_a\) 最大的点与 \(p_{1}\) (q的下标是2)匹配,并将之删除,以此类推,便得到所求。
维护的方式比较困难,我这里用 \(upper\_bound\) 查找 \(q_r\), \(map\) 使 \(p、q\) 同步删除,线段树维护 \(q_1...q_r\) 中 \(q_a\) 最大的点。这样子就有了一个总复杂度为 \(O(n \log n)\) 的 check 了。
代码如下
// code by 樓影沫瞬_Hz17
#include <bits/extc++.h>
#define int long long
using namespace std;
const int N = 1e5 + 10;
struct node {
int a, b, id;
} q[N], p[N];
typedef pair<int, int> pii;
bool vis[N];
int n;
struct SegmentTree{
int ma[N << 2], id[N << 2]; // 维护下标,维护最大值也是为了维护下标
inline void up(int u) {
ma[u] = max(ma[u << 1], ma[u << 1 | 1]);
if(ma[u << 1] == ma[u << 1 | 1]) {
id[u] = max(id[u << 1], id[u << 1 | 1]);
}
else if(ma[u] == ma[u << 1]) id[u] = id[u << 1];
else id[u] = id[u << 1 | 1];
}
inline void build(int u, int l, int r) {
if(l == r) {
ma[u] = q[l].a;
id[u] = l;
return;
}
int m = (l + r) >> 1;
build(u << 1, l, m);
build(u << 1 | 1, m + 1, r);
up(u);
}
inline void delate(int u, int l, int r, int p) {
if(l == r) {
ma[u] = id[u] = 0;
return;
}
int m = (l + r) >> 1;
if(m >= p) delate(u << 1, l, m, p);
else delate(u << 1 | 1, m + 1, r, p);
up(u);
}
inline pii query(int u, int l, int r, int L, int R) { // 查询没有被删掉的最大值的下标
if(L <= l and R >= r)
return {ma[u],id[u]};
if(L > r || l > R) return {0, 0};
int m = (l + r) >> 1;
pii t1 = query(u << 1, l, m, L, R), t2 = query(u << 1 | 1, m + 1, r, L, R);
if(t1.first == t2.first) return t1.second > t2.second ? t1 : t2;
else if(t1.first > t2.first) return t1;
else return t2;
}
} T;
map <int, int> mp; // 将两个下标联合起来
bool operator < (int a, node b) { return a < b.b; } // upper_bound 要用
inline bool check(int mid) {
memset(vis, 0, sizeof vis);
T.build(1, 1, n * 2);
for(int i = 1, cnt = 1; cnt <= n; /* cnt 表示对数 */cnt ++) { // 这一点代码细节比较多
while(vis[mp[p[i].id]]) i ++; // 如果 vis 过就跳
int t = mid - p[i].a;
if(t < p[i].b) return 0; // 若一个数自己就够大了就直接返回0
vis[mp[p[i].id]] = 1;
T.delate(1, 1, n * 2, mp[p[i].id]); // 先删掉pi
int r = upper_bound(q + 1, q + 1 + n + n, t) - q - 1;// 找出令x合法的y
if(r <= 0) return 0;// 若找不出,返回0
int pos = T.query(1, 1, n * 2, 1, r).second; // 找出最大的 qa
if(pos == 0) return 0;//如果都杀干净了就返回0
vis[pos] = 1;
T.delate(1, 1, n * 2, pos);// 杀掉
}
return 1;
}
signed main() {
ios::sync_with_stdio(0);
freopen("magic.in", "r", stdin);
freopen("magic.out", "w", stdout);
cin >> n;
for(int i = 1; i <= 2 * n; i ++)
cin >> p[i].a, p[i].id = i;
for(int i = 1; i <= 2 * n; i ++)
cin >> p[i].b, q[i] = p[i];
sort(p + 1, p + 1 + n * 2, [](node a, node b) { return a.a > b.a;});
sort(q + 1, q + 1 + n * 2, [](node a, node b) { return a.b < b.b;});
for(int i = 1; i <= n * 2; i ++) mp[q[i].id] = i;
int l = 0, r = 2e9;
while(l <= r) {
int mid = (l + r) >> 1;
if(check(mid)) r = mid - 1;
else l = mid + 1;
}
cout << l;
}

浙公网安备 33010602011771号