问题 B: 【数论】密码

提交
大佬博客
分析:比较难的一道数学题.有两个结论: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 ;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值