您的位置:首页 > 其它

Mike and gcd problem-codeforces-Round 410 Div2-C

2017-04-22 19:52 579 查看

题意

给你n个数(2 ≤ n ≤ 100 000) ,A = [a1, a2, …, an] 。可以对第i个数进行操作(1 ≤ i < n),使a[i],和a[i+1]变为a[i]-a[i+1]和a[i]+a[i+1]。问你最少经过多少次操作能使gcd(a[1],…,a
)>1。能输出YES和最小的操作数,不能输出NO。

题解

先判断原序列最小公因子是否大于1,如果不是,则一定会改成最小公因子为偶数的序列。

证明:

假设两个公因子不包括奇数k的数 a,b

经过修改后最小公因子变为奇数 k

那么也就是 (a-b)%k==0&&(a+b)%k==0

通过前式可以知道a和b是膜k同余的,a≡b(%k),假设a%k==x(x>=0&&x<k)

那么(a+b)%k==(2*x)%k,根据后式可知(a+b)%k==0,因为k是奇数,所以x的取值只能为0。而这和数 a,b不包括奇数k公因子矛盾,所以得证。

接下来就是贪心的修改,使得序列变成全偶数。因为一次修改是a[i]-a[i+1]和a[i]+a[i+1],两次修改是-2a[i+1]和2a[i]。所以O(n)分奇偶讨论即可。

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 1e5+5;
ll n,sz[maxn];

ll gcd(ll a,ll b)
{
return b==0 ? a : gcd(b,a%b);
}

int main()
{
scanf("%lld",&n);
for(int i=1;i<=n;i++) scanf("%lld",&sz[i]);
ll g = sz[1];
for(int i=2;i<=n;i++)
{
g = gcd(g,sz[i]);
}
if(g>1)
{
printf("YES\n0\n");
return 0;
}
ll ans=0;
for(int i=1;i<n;i++)
{
if(sz[i]&1)
{
if(sz[i+1]&1)
{
ans++;
sz[i+1] += sz[i];
}
else
{
ans += 2;
sz[i+1] = 2*sz[i];
}
}
}
if(sz
&1) ans += 2;
printf("YES\n%I64d\n",ans);
return 0;
}


关键是想明白如果不大于1最后一定会改成最小公因子为偶数的序列,想明白后代码还蛮简单的。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: