Gravatar
终焉折枝
积分:1506
提交:201 / 363

Pro3579  [统一省选 2021]卡牌游戏


更好的阅读体验:https://www.cnblogs.com/To-Carpe-Diem/p/19635009


大意

给出 $n$ 张卡牌,每张卡牌都有两个权值 $a_i$ 和 $b_i$,分别对应的是正面和反面,求在至少翻 $m$ 张牌,然后求出最小的极差。


思路

我们考虑这样的事,首先,我们不考虑每一张牌的情况,我们只考虑这个先处理极差的问题,我们先把这 $2n$ 张牌记录一下类型,然后将其排序。

排完序之后,我们需要的是选择一段区间 $[L, R]$,使得 $[L, R]$ 的区间包含所有的类型,且其中间反转的牌不超过 $m$ 个,那么这个区间的 $val_R - val_L$ 就是一个合法的答案,我们要想使得这个值尽可能的小,我们不妨使用双指针的写法,固定左端点,向右查询合法右端点。

这个过程是具有单调性的,你的 $L$ 向右,$R$ 也必定向右,具体过程是这样的,如果当前的区间不合法,那么就让 $R$ 右移,使其包含所有的 $n$ 个情况,一旦包含足够,就判断是否合法,这个过程是可以在扫描的过程中动态处理的,这步判断是 $\mathcal{O}(1)$ 的,如果是合法就将 $L$ 向右收缩,这样去看看有没有更优的 $val_R - val_L$。


代码

#include<iostream>
#include<algorithm>
using namespace std;

const int MAXN = 1e6 + 5;
int n, m, l, r, cnt = 0;
struct node{
    long long val, id;
    bool op;
}k[MAXN << 1];
int t[MAXN << 1];
bool cmp(node x, node y){
    return x.val < y.val;
}

int main(){
    // freopen("card.in", "r", stdin);
    // freopen("card.out", "w", stdout);
    cin.tie(0) -> ios::sync_with_stdio(0);
    cin >> n >> m;
    for(int i = 1;i <= n;i ++){
        cin >> k[i].val;
        k[i].id = i;
        k[i].op = 1;
    }
    for(int i = 1;i <= n;i ++){
        cin >> k[i + n].val;
        k[i + n].id = i;
        k[i + n].op = 0;
    }
    sort(k + 1, k + 2 * n + 1, cmp);
//    for(int i = 1;i <= n * 2;i ++){
//        cout << k[i].val << ' ' << k[i].id << ' ' << k[i].op << '\n';
//    }
    for(l = 0;cnt + k[l + 1].op <= m && !t[k[l + 1].id];l ++){
        cnt += k[l + 1].op;
        t[k[l + 1].id] = 1;
    }
    for(r = 2 * n + 1;cnt + k[r - 1].op <= m && !t[k[r - 1].id];r --){
        cnt += k[r - 1].op;
        t[k[r - 1].id] = 1; 
    }
    long long ans = 1e9 + 7;
	while(l >= 0) {
		ans = min(k[r - 1].val - k[l + 1].val, ans);
		t[k[l].id] = 0;
		cnt -= k[l].op;
		l --;
        for(r;cnt + k[r - 1].op <= m && !t[k[r - 1].id];r --){
            cnt += k[r - 1].op;
            t[k[r - 1].id] = 1; 
        }
	}
	cout << ans << '\n';
}




2026-02-24 20:22:45    
我有话要说
暂无人分享评论!