CF1305F Kuroni and the Punishment
Posted Jozky86
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF1305F Kuroni and the Punishment相关的知识,希望对你有一定的参考价值。
CF1305F Kuroni and the Punishment
题意:
给定 n 个数。每次可以选择将一个数 +1 或 -1 。求至少多少次操作使得整个序列都是正数且全部元素的 gcd>1 。
n
<
=
2
e
5
,
a
i
<
=
1
0
12
n<=2e5,a_{i}<=10^{12}
n<=2e5,ai<=1012
题解:
首先不难想到,我们可以让所有数变成偶数,这样gcd为2,这样的话,每个数的操作次数小于等于1,这是答案的上界n,也就是最后的答案只会比n优,不可能比n劣
由上面这个结论我可以推出一个定理:在最终的方案中,指定存在至少一半的元素,他们最多被操作过一次
证明:如果存在一般元素被操作了超过一次,那么操作次数就是
⌈
n
+
1
2
⌉
∗
2
>
n
\\lceil \\frac{n+1}{2}\\rceil*2>n
⌈2n+1⌉∗2>n,这超过了答案上界
所以,如果我们随机选任意元素,这个元素至少有
1
2
\\frac{1}{2}
21的概率被最多操作一次。对于数x最多操作一次,可以得到x-1,x,x+1,也就是x最终有这个三个形态。因为gcd要>=2,说明x-1/x/x+1的某个因子就是我们要找的答案,那我们直接因子分解,对于每个因子w去强行判断如果w是gcd所需要的操作次数,然后取min
为什么这样是对的?
我们刚才说了,操作次数小于等于1的概率是>=
1
2
\\frac{1}{2}
21的,所以我们随机选取k次,那么错误的概率就是
1
2
k
\\frac{1}{2^k}
2k1
当k>=30时一般不会出错
还有随机不要用rand(),详细原因详见如何正确地生成一个随机数
代码:
#include<bits/stdc++.h>
using namespace std;
mt19937 rnd(chrono::system_clock::now().time_since_epoch().count());
typedef long long ll;
ll a[200005];
int n;
ll cal(ll x)
{
ll ret=0;
for (int i=0;i<n;i++)
{
ll tmp=a[i]%x;
if (a[i]!=tmp)
ret+=min(tmp,x-tmp);
else
ret+=x-tmp;
}
return ret;
}
ll fac(ll x)
{
ll ret=1e18;
ll en=sqrt(x+1ll);
for(ll i=2;i<=en;i++)
{
if (x%i==0)
{
ret=min(ret,cal(i));
while(x%i==0)
x/=i;
if (x==1)
break;
}
}
if (x>1)
ret=min(ret,cal(x));
return ret;
}
int main()
{
srand(time(0));
cin>>n;
for(int i=0;i<n;i++)
scanf("%I64d",&a[i]);
int T=50;
ll ans=1e18;
while (T--)
{
ll pos=rnd()%n;
if (a[pos]>2)
ans=min(ans,fac(a[pos]-1ll));
ans=min(ans,fac(a[pos]));
ans=min(ans,fac(a[pos]+1ll));
}
cout<<ans;
}
以上是关于CF1305F Kuroni and the Punishment的主要内容,如果未能解决你的问题,请参考以下文章
CF1305F Kuroni and the Punishment (随机化)
CF1305D Kuroni and the Celebration
CF1305E Kuroni and the Score Distribution
cf 1305 E. Kuroni and the Score Distribution