版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/winter2121/article/details/81978422
问题 B: 【数论】密码
时间限制: 4 Sec 内存限制: 128 MB
提交: 60 解决: 17
[提交] [状态] [讨论版] [命题人:admin]
题目描述
有一个密码箱,0到n-1中的某些整数是它的密码。且满足:如果a和b都是它的密码,那么(a+b)%n也是它的密码(a,b可以相等,%表示整除取余数),某人试了k次密码,前k-1次都失败了,最后一次成功了。
问:该密码箱最多有多少不同的密码。
输入
第一行两个整数分别表示n,k(1≤k≤250000,k≤n≤1014)。第二行为k个用空格隔开的非负整数,表示每次试的密码。数据保证存在合法解。
输出
输出一行一个数,表示结果。
样例输入
42 5
28 31 10 38 24
样例输出
14
【分析】
由题意得:若x是密码,则x的倍数一定是密码,kx%n一定是密码,即kx-tn=?这个正数一定是密码。
拓展欧几里得指出:kx-tn=gcd(x,n)一定存在可行的k和t使得式子成立,故gcd(x,n)一定是密码。
推论:若x不是密码,则gcd(x,n)一定不是(因为gcd(x,n)是x的因子),所以gcd(x,n)的因子也一定不是密码。
由推论可知 对于1<=i<k,gcd(a[i],n)的因子一定不是密码。
由于gcd(a[k],n)是密码,所以其因子只要不是gcd(a[i],n) (1<=i<k)的因子,就可以作为密码。
找到这样的最小的那个因子,将其作为密码,故其倍数全部是密码。
【代码】
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAX=5e5+4;
ll gcd(ll a,ll b){while(b)b^=a^=b^=a%=b;return a;}
ll a[MAX];
ll n,k,ak;
int check(int x)
{
for(int i=1;i<=k;i++)
{
if(a[i]%x==0)return 1;
}
return 0;
}
int main()
{
scanf("%lld%lld",&n,&k);
for(int i=1;i<=k;i++)scanf("%lld",&a[i]);
for(int i=1;i<=k;i++)a[i]=gcd(a[i],n);
ak=a[k];
sort(a+1,a+k);
k=unique(a+1,a+k)-a-1; //去重
ll sq=sqrt(ak+0.5),ans=0;
for(int i=1;i<=sq;i++)if(ak%i==0)
{
if(check(i)==0)
{
ans=n/i;
break;
}
else if(check(ak/i)==0) //此时不要break,可能有小于sq的数满足
{
ans=n/ak*i;
}
}
cout<<ans<<endl;
}