2277 密码
有一个密码箱,0到n-1中的某些整数是它的密码。且满足:如果a和b都是它的密码,那么(a+b)%n也是它的密码(a,b可以相等,%表示整除取余数),某人试了k次密码,前k-1次都失败了,最后一次成功了,问,该密码箱最多有多少不同的密码
对于二元一次不定方程,形式为ax+by=c其中a,b,c是整数,a×b≠0,此方程有整数解的成分必要条件 是gcd(a,b)|c
这个问题有两个结论
1.如果x是密码,那么gcd(x,n)也是密码
2.如果x,y是密码,那么gcd(x,y)也是密码,根据这两个结论,就能轻松地解决这个题了
先来证明第一个结论
构造一个方程xk-nc=gcd(x,n)也就是把xk%n写成了不是取模的形式,x×k减掉其中n的倍数;这个方程一定有解,也就是存在一个k使得xk%n=gcd(x,n),而x是密码,(x+x)%n也是密码,所以kx%n也是密码,那么gcd(x,n)就是密码
接着来证明结论2
通过一系列的推论就能证明
所以
对于任意一个密码集合A,分析:
1.设A中所有数的GCD为x
2.得到x属于A
3.如果密码集合中有比x小的数y,则gcd(x,y)<x不符合规定,所以x是A中最小的数
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cmath>
using namespace std;
typedef long long ll ;
const int N = 250010 ;
ll n , k , a[N] , ans , cnt ;
bool check(int x)
{
for(int i = 1 ;i <= cnt ;i ++)
if(a[i] % x == 0)
return false ;
return true ;
}
ll gcd(ll a , ll b)
{
return b == 0 ? a : gcd(b , a % b) ;
}
int main()
{
scanf("%lld%lld" , &n , &k) ;
for(int i = 1 ;i <= k ;i ++)
{
scanf("%lld" , &a[i]) ;
a[i] = gcd(a[i] , n) ;
}
sort(a + 1 , a + k) ;
for(int i = 1 ;i < k ; i ++)
if(a[i] != a[i - 1])
a[++ cnt] = a[i] ;
for(ll i = 1 ;i <= sqrt(a[k]) ;i ++)
if(a[k] % i == 0 )
{
if(check(i))
{
ans = n / i ;
break ;
}
else if(check(a[k] / i))
ans = n / a[k] * i ;
}
printf("%lld\n" , ans) ;
return 0 ;
}