题意:给一个序列a1到an ,如果gcd(a1,a2,...an)≠1,给一种操作,可以使ai和ai+1分别变为(ai+ai+1)和(ai-ai+1);问需要执行几次这个操作才能使得gcd(a1,a2,...an)>1.
分析:
1.首先,答案总是YES。
2,假设gcd(a1,a2,...an)=1,在一次对ai和ai+1的操作后新的gcd为d,则d满足:d|ai - ai + 1 and d|ai + ai + 1 d|2ai and d|2ai + 1.。同样,因为d是新序列的gcd,所以d也满足: d|aj, j ≠ i, i + 1.
3.从而我们得到:。所以gcd(a1,a2,...an)=1,则在一次操作后,序列的gcd最多会变得两倍大.(即会变成2.)
4.这意味着如果我们要是序列的gcd大于1的话只用使所有的数为偶数即可。
5.因为两个奇数只通过一次操作即都可以变成偶数,而一奇一偶要通过两次才可以都变成偶数。
6.可以把题目转化为:给一个序列a1到an ,如果gcd(a1,a2,...an)≠1,求将所有数变成偶数需要的操作次数
7.又可转化为:求序列中有几段连续为奇数,如果使奇数段中含的数为偶数个k,则需要k/2次,如果为奇数个,则需要(k/2)+2次;
代码:
/* Problem: C. Mike and gcd problem Time: 2017/5/5/19:21 */ #include<iostream> #include<cstdio> #include<cstring> #include<string> #include<map> #include<vector> #define ll long long #define inf 100000000 using namespace std; ll a[100005]; ll gcd(ll a,ll b) { return b==0?a:gcd(b,a%b); } int main() { int n; scanf("%d",&n); for(int i=0;i<n;i++) scanf("%I64d",a+i); ll d=a[0]; for(int i=1;i<n;i++) d=gcd(d,a[i]); int ans=0; if(d<=1) { int odd=0; for(int i=0;i<n;i++) { if(a[i]%2!=0) odd++; else if(odd!=0) { ans+=(odd%2==0?(odd/2):(odd/2+2)); odd=0; } } //cout<<ans<<endl; if(odd) ans+=(odd%2==0?(odd/2):(odd/2+2)); } printf("YES\n"); printf("%d\n",ans); return 0; }
补充:(关于DIVISIBILITY AND GREATEST COMMON DIVISORS)
几个定理:
1.. Let a, b ∈ Z with a|b. Then a|bc for any c ∈ Z.
2. If a|b and b|c then a|c.
3.If a|b and a|c then a|(br + cs) for every r and s in Z. In particular, if a|b and a|c then a|(b + c) and a|(b − c).
4.For nonzero a and b in Z, there are x and y in Z such that (3.2) (a, b) = ax + by.