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;
}

浙公网安备 33010602011771号