题目:传送门
题意:给你 n 个数,然后你每次操作可以选择任意一个数 a[ i ],让它加 1 或者减 1,问最少需要操作几次,可以使得所有 a[ i ] 的 gcd 大于 1
2 <= n <= 2e5, 1 <= ai <= 1e12
思路:
考虑最后的 gcd = 2,那这样的话只要把所有奇数加 1 变成偶数就行了,这样的操作数最多是 n 次,也就是每个 a[ i ] 都操作一次。
那如果还有更优的答案的话,那至少有 n / 2 个数是操作 1次或者0次的,因为你最多可以有 n / 2 个操作两次的。
这样的话,你随机的在 n 个 a[ i ] 中选择一个数,那么最终的 gcd 是 a[ i ] 或 a[ i ] - 1 或 a[ i ] + 1 的某个质因子的概率是 1 / 2。
因为你至少有 n / 2 个数是操作一次或者零次的嘛,那我们随机的选择某个 a[ i ],假设它是操作一次或者零次的那个数。
这样的话, 假设我们随机选择了 100 个 a[ i ],那它们都不是操作一次或者零次的数的概率是 ( 1 / 2 ) ^ 100,这是一个很小的数。
#include <bits/stdc++.h> #define LL long long #define mem(i, j) memset(i, j, sizeof(i)) #define rep(i, j, k) for(int i = j; i <= k; i++) #define dep(i, j, k) for(int i = k; i >= j; i--) #define pb push_back #define make make_pair #define INF INT_MAX #define inf LLONG_MAX #define PI acos(-1) #define fir first #define sec second using namespace std; const int N = 1e6 + 5; int pre[N], vis[N], tot, n; LL a[N]; set < LL > Q; void init() { rep(i, 2, N - 5) { if(!vis[i]) pre[++tot] = i; rep(j, 1, tot) { if(i * pre[j] > N - 5) break; vis[i * pre[j]] = 1; if(i % pre[j] == 0) break; } } } void add(LL x) { for(int i = 1; i <= tot && x > 1 && pre[i] * pre[i] <= x; i++) { if(x % pre[i] == 0) { Q.insert(pre[i]); while(x % pre[i] == 0) { x /= pre[i]; } } } if(x > 1) Q.insert(x); } LL solve(LL x) { LL res = 0; rep(i, 1, n) { LL add = (a[i] < x ? x - a[i] : min(a[i] % x, x - a[i] % x)); res = min(1LL * n, res + add); } return res; } int main() { init(); scanf("%d", &n); rep(i, 1, n) scanf("%lld", &a[i]); shuffle(a + 1, a + 1 + n, default_random_engine (time(0))); rep(i, 1, min(100, n)) { add(a[i]); add(a[i] + 1); if(a[i] > 1) add(a[i] - 1); } LL ans = n; for(LL v : Q) { ans = min(ans, solve(v)); } printf("%lld ", ans); return 0; }