CF1305F Kuroni and the Punishment
有意思的随机化题~~~
首先考虑一种简单的情况:把所有的数变为2的倍数,此时答案为序列中奇数的个数,最大为n,因此不用考虑答案>n的方案啦
如果最优的方案使>n/2的数要进行>=2次的操作,那最终答案显然>n,因此不可能为最优解,得出结论:最优方案中,只要进行<2(0或1)次操作的数的个数p>=n/2
然后开始玄学随机:随机选取m个数,当前选取的数为x,对x-1,x,x+1进行质因数分解(事实上分解因数就行),然后对分解出的因数分别计算一次答案(怎么算显然),只要x处于这p个数中就能得出正确答案
选取的每个x不在p个数中的概率最多为1/2,即选取m个数答案错误的概率为2的\(\frac{1}{2^m}\),讲道理选30个数就足够,但由于我的随机可能并不十分随机,选了100个才通过
#include <bits/stdc++.h>
using namespace std;
#define int long long
inline void read (int &x) {
char ch = getchar(); x = 0;
while (!isdigit(ch)) ch = getchar();
while (isdigit(ch)) x = x * 10 + ch - 48, ch = getchar();
} const int N = 2e5 + 5, mx = 1e6;
int n, a[N], p[N], k[mx + 5], tot, res = 1e9, used[N];
inline void Pre_work () {
for (int i = 2; i <= mx; ++i) {
if (!k[i]) ++tot, p[tot] = i;
for (int j = 1; j <= tot && i * p[j] <= mx; ++j) {
k[i * p[j]] = 1; if (i % p[j] == 0) break;
}
}
} vector <int> vc;
inline void add (int x) {
if (!x) return;
for (int i = 1; i <= tot && p[i] <= x; ++i)
if (x % p[i] == 0) {
if (!used[i]) vc.push_back (p[i]);
while (x % p[i] == 0) x /= p[i]; used[i] = 1;
}
if (x > 1) vc.push_back (x);
}
#define Min(a, b) (a > b ? b : a)
inline int work (int x) {
int tmp = 0;
for (int i = 1; i <= n; ++i)
tmp += (a[i] < x) ? x - a[i] : Min (a[i] % x, x - a[i] % x);
return tmp;
}
signed main() {
read (n); Pre_work(); srand (time(NULL));
for (int i = 1; i <= n; ++i) read (a[i]);
random_shuffle (a + 1, a + n + 1);
for (int i = 1; i <= 100 && i <= n; ++i)
add (a[i]), add (a[i] - 1), add (a[i] + 1);
for (int i = 0; i < vc.size(); ++i) res = Min (res, work (vc[i]));
return printf ("%lld\n", res), 0;
}