peiwenjun's blog 没有知识的荒原

CF1305F Kuroni and the Punishment 题解

题目描述

给定 \(n\) 个数 \(a_i\) ,每次操作可以选择一个数 \(+1\)\(-1\) ,求最小操作次数使得所有数为正且 \(\gcd\gt 1\)

数据范围

  • \(2\le n\le 2\cdot 10^5,1\le a_i\le 10^{12}\)

时间限制 \(\texttt{2.5s}\) ,空间限制 \(\texttt{256MB}\)

分析

容易发现答案不超过 \(n\) ,因此至少有一半的数满足 \(a'_i\in\{a_i-1,a_i,a_i+1\}\)

注意到 \(\gcd\gt 1\) 等价于存在一个质因子 \(p\) 满足所有数都是 \(p\) 的倍数。

对于给定的质因子 \(p\) ,我们可以在 \(\mathcal O(n)\) 的时间内统计让所有数变成 \(p\) 的倍数的最小操作次数。

在序列中随机选择 \(k\) 个数,将其分解质因数,枚举 \(a_i-1,a_i,a_i+1\) 的质因子 \(p\) 加入候选集合。

由于 \(a_i\le 10^{12}\) ,因此 \(\omega(a_i)\le11\) ,候选集合中至多只有 \(33k\) 个质因子。

对这些质因子统计答案,时间复杂度 \(\mathcal O(k\frac{\sqrt v}{\log v}+33kn)\)

当且仅当最优策略不包含上述 \(k\) 个数时会出错,出错概率 \(\le\frac 1{2^k}\) ,取 \(k=30\) 即可通过。

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e6+5,inf=1e18;
int n,cnt,res=inf;
int a[maxn],b[maxn],p[maxn];
set<int> s;
mt19937 rnd(random_device{}());
void init(int n)
{
    for(int i=2;i<=n;i++)
    {
        if(!b[i]) p[++cnt]=i;
        for(int j=1;j<=cnt&&i*p[j]<=n;j++)
        {
            b[i*p[j]]=1;
            if(i%p[j]==0) break;
        }
    }
}
void calc(int n)
{
    if(!n) return ;
    for(int i=1;i<=cnt&&p[i]*p[i]<=n;i++)
    {
        if(n%p[i]) continue;
        s.insert(p[i]);
        while(n%p[i]==0) n/=p[i];
    }
    if(n!=1) s.insert(n);
}
void work(int p)
{
    int cur=0;
    for(int i=1;i<=n;i++)
    {
        int x=a[i]/p;
        cur+=min(x?a[i]-x*p:inf,(x+1)*p-a[i]);
        if(cur>=res) return ;
    }
    res=cur;
}
signed main()
{
    scanf("%lld",&n),init(1e6);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    shuffle(a+1,a+n+1,rnd);
    for(int i=1;i<=min(n,30ll);i++) calc(a[i]-1),calc(a[i]),calc(a[i]+1);
    for(auto p:s) work(p);
    printf("%lld\n",res);
    return 0;
}

posted on 2023-06-12 11:44  peiwenjun  阅读(7)  评论(0)    收藏  举报

导航