abc sock2题解(翻译)

以下是翻译

首先,将他没有丢过的袜子配对是最佳选择。
证明
对于他没有用过的颜色 p,考虑一种情况,即两只颜色为 p 的袜子没有配对。当它们分别与颜色为 A i 和 A j 的袜子配色时,根据三角形不等式 ∣A i -A p ∣+∣A j -A p ∣≥∣A i -A j ∣=∣A i -A j ∣+∣A p -A p ∣、 因此,将(A p ,A p )和(A i ,A j )配对,而不是将(A p ,A i ),(A p ,A j )配对,总的怪异度并不会增加。当一只颜色为 p 的袜子没有与任何袜子配对,而另一只袜子与另一只颜色为 A i 的袜子配对时,总怪异度不会因为(A p ,A p )与 A i 不配对而增加,也不会因为(A p ,A i )与 A p 不配对而增加。因此,我们可以假设在最优解中 A p 颜色的两只袜子总是配对的。
因此,这个问题可以看作是要求从 A 1、A 2、…、A K 每种颜色的袜子中做出 ⌊ 2 K ⌋ 对的问题。如果 K 是偶数,最佳的配对似乎是 (A 1 ,A 2 ),(A 3 ,A 4 ),…,(A K-1 ,A K ),这样相邻的颜色(在排序序列中)就能配对,事实也的确如此。
问题在于当 K 为奇数时。在这种情况下,我们可以对唯一没有配对的颜色进行蛮力计算,这样就能找到其他袜子配对的最佳方法,就像我们对偶数 K 所做的那样。当唯一没有配对的颜色固定不变时,其余袜子配对时的最小总怪异度可在 O(N) 时间内天真地找到,但它导致的总复杂度为 O(N 2)。相反,我们可以预先计算前几只袜子配对时的总怪异度,如 presum[2]=(A 2 -A 1 ),presume[4]=(A 4 -A 3 )+(A 2 -A 1 ),presume[6]=(A 6 -A 5 )+(A 4 -A 3 )+(A 2 -A 1 ),…,用前缀和的方式;也可以求 “后缀和”。因此,用这种方法解题总共只需 O(N) 时间。
额外的好处:当 K 为奇数时,我们可以只尝试 A 1 ,A 3 ,A 5 ,…,A K(证明略),而不用蛮力求出所有未配对的袜子。在下面的示例代码中,我们采用这种方法来简化实现。

以下是参考代码

// Problem: #B. 袜子
// Contest: Hydro
// Memory Limit: 256 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define ll long long
#define fst                      \
    ios::sync_with_stdio(false); \
    cin.tie(0);                  \
    cout.tie(0);
#define f(x, y, z) for (register ll x = (y); x <= (z); x++)
using namespace std;
const long long INF = 0x3f3f3f3f3f3f3f3f, N = 5e5;
ll n, k;
ll a[N];
ll qz[N];
ll ans = INF;
signed main()
{
    cin >> n >> k;
    {
        unordered_map<ll, int> u;
        f(i, 1, k)
        {
            ll tmp;
            cin >> tmp;
            u[tmp]++;
        }
        ll top = 0;
        f(i, 1, n)
        {
            if (!u.count(u[i]))
            {
                a[++top] = i;
                a[++top] = i;
            }
            else if (u[i] == 1)
                a[++top] = i;
        }
        n = top;
    }
    //f(i, 1, n) cout << a[i] << " ";
    qz[1] = a[1];
    f(i, 2, n)
    {
        qz[i] = qz[i - 2] + a[i] - a[i - 1];
    }
    // {
    // puts("");
    // f(i, 1, n) cout << qz[i] << " ";
    // }
    if (n % 2 == 0)
        return cout << qz[n], 0;
    f(i, 1, n)
    {
        ll k;
        if (i % 2 == 1)
            k = qz[i - 1] + qz[n] - qz[i];
        else
            k = qz[i - 2] + a[i + 1] - a[i - 1] + qz[n] - qz[i + 1];
        //cerr << i << "::" << k << endl;
        ans = min(ans, k);
    }
    cout << ans;
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值