Y
K
N
U
F

题解 实战教学(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;
}

posted @ 2025-08-03 14:54  樓影沫瞬_Hz17  阅读(31)  评论(0)    收藏  举报