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

猜你喜欢

转载自www.cnblogs.com/whx666/p/12675090.html
今日推荐