提交
大佬博客
分析:比较难的一道数学题.有两个结论:1.如果x是密码,那么gcd(x,n)也是密码. 2.如果x,y是密码,那么gcd(x,y)也是密码.根据这两个结论就能很轻松地解决本题了.
先来证明第一个结论:构造二元一次不定方程xk - nc = gcd(x,n),(也就是把x * k % n 写成了不是取模的形式,x * k 减掉其中n的倍数 )这个方程是一定有解的,也就是说一定存在一个 k使得xk%n = gcd(x,n).而x是密码,(x+x)%n也是密码,所以kx%n也是密码,那么gcd(x,n)就是密码.x就是题目中告诉的a[k].
再来证明第二个结论:gcd(x,y) = ax + by,a,b有可能小于0.根据结论一可以推出
(px + qy)%n是密码(p,q ≥ 0). 由ax + by ≡ gcd(x,y)(mod n),变形一下可以得到:
ax + by ≡ ax + by + pnx + qny(mod n) --> (a + pn) * x + (b + qn) * y ≡ gcd(x,y)(mod n).根据假设,((a + pn) * x + (b + qn) * y) % n是密码(x,y系数大于0),那么gcd(x,y)也是密码.
把所有密码写出来以后,可以发现是一个x,2x,3x…的形式,所以任务就是找到一个最小的x使得x整除gcd(a[k],n).同时这个x不能整除a[j](1 ≤ j < k).那么x就是gcd(a[k],n)的因子,根号的时间处理出来然后进行判断.判断的话也有一个优化,如果y是密码,gcd(x,y)不是密码,那么x也不是密码,所以在判断的时候看一下所有的gcd(a[j],n)是否被当前的因子整除就行了.
#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 ;
}