CF1665B Array Cloning Technique 题解

题意简述

给定长度为 $n$ 的数组 $a$。

  • 操作 1:从已有的数组里复制一份

  • 操作 2:从已有的所有数组里交换任意两个数的位置

求将一个数组都变为相同的元素所需的的最少步数。

Sol

操作 1 有 $p$ 次,操作 2 有 $q$ 次。

不难发现,最后选定的数在原数组出现次数越多,操作数越少,所以最后数组的元素都为原数组的众数(或之一)。

不妨设原数组中一个众数 $x$ 出现的次数为 $s$。

我们要做的是:复制数组,并使所有 $x$ 交换到同一个数组里。

忽略 $p$ 的值,研究 $q$ 的值。原问题转化为将原数组除 $x$ 以外的数都换为 $x$。

显然在原数组内,除 $x$ 以外的数共有 $n-s$ 个,因此 $ q = n - s $。

同时考虑 $p,q$ 的值。要使 $p$ 的值尽可能小,那么就要使待复制数组中 $x$ 的数量尽可能多。由于 $q$ 的值与操作顺序无关,那么就可以将当前所有 $x$ 转到同一数组内,然后复制该数组,这样就能使得每次复制尽可能多的 $x$。

每次复制都使 $x$ 的总个数乘 2,当 $x$ 的总个数大于等于 $n$ 时停止复制。即 $p$ 为满足 $ s \times 2^p \ge n $ 的最小值。

把 $s$ 除过去,再对不等式两遍取个对数:

$$p \ge \log_2\dfrac{n}{s}$$

将上式化为等式:

$$p = \lceil \log_2\dfrac{n}{s} \rceil$$

答案 $ans = p + q = \lceil \log_2\dfrac{n}{s} \rceil + n - s$

计算众数可以用 map(好像也可以排序+双指针),然后按上面的柿子输出即可。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<map>
#define ll long long

using namespace std;

ll a[100010];

map <ll,ll> f;

int main(){
    ll t;
    cin >> t;
    for(ll i = 1; i <= t; i++){
        memset(a,0,sizeof(a));
        f.clear();
        ll n;
        cin >> n;
        for(ll j = 1; j <= n; j++){
            cin >> a[j];
            f[a[j]]++;
        }

        ll s = -1;

        for(ll j = 1; j <= n; j++){
            s = max(f[a[j]],s);
        }

        // 这里别忘了乘1.0转小数
        cout << ceil(log2(1.0*n/s)) + n - s << endl;

    }
    return 0;
}
posted @ 2022-04-09 09:33  象征阳光  阅读(11)  评论(0)    收藏  举报  来源