upc数学一本通【数论】密码(欧几里得gcd的奥妙)

版权声明:本文为博主原创文章,未经博主允许不得转载。 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;
}

猜你喜欢

转载自blog.csdn.net/winter2121/article/details/81978422