·问题描述·
有一个密码箱,0到n-1中的某些数是它的密码。且满足:如果a和b都是它的密码,那么(a+b)%n也是它的密码。某人试了k次密码,前k-1次都失败了,最后一次成功。
问:该密码箱最多有多少个密码?
·输入格式·
输入第一行两个整数分别表示n,k。
第二行为k个用空格隔开的非负整数,表示每次试的密码。(数据保证存在合法解)
·输出格式·
输出一行一个数,表示结果。
·输入样例·
42 5
28 31 10 38 24
·输出样例·
14
·数据范围·
对于100%的数据:1<=k<=250000,k<=n<=10^14。
Solution:
本题考察数学。由题意可知,若x为密码则(x+x)%n为密码,则p*x%n(0<p<n)也为密码。而对于p*x%n=q,等价于p*x-n*c=q。
由引理:a*x+b*y=c(均为整数),有整数解的充要条件是gcd(a,b)|c。证明很容易:不妨设a=p*gcd(a,b),b=q*gcd(a,b) --> a*x+b*y=(p+q)*gcd(a,b)=c,显然要有整数解,则gcd(a,b)|c。
回到本题的条件:p*x-n*c=q。有解则必定满足gcd(x,n)|q,所以必定有p*x-n*c=gcd(x,n)成立,等价于p*x%n=gcd(x,n),则gcd(x,n)一定为一个密码。类似的,对于不同的密码x和y,存在(p*x+q*y)%n为密码,由引理必定存在p*x+q*y=gcd(x,y),与单个x是密码同理gcd(x,y)一定是密码。
而要使得密码最多,由x是密码则p*x%n(0<p<n)为密码可知,当x为最小时,密码最多有n/x个。
具体实现时,我们先求出a[k]=gcd(a[k],n),再使a[i]=gcd(a[i],a[k]),然后从新的a[k]中删去所有是a[i]因子的因子,最后输出答案就是n除以没被删的最小的因子。
代码:
1 /*数学一本通上的例题——by 520*/ 2 #include<bits/stdc++.h> 3 #define il inline 4 #define ll long long 5 using namespace std; 6 ll n,k,tot,a[250005],q[250000],p[250000],cnt=1; 7 il ll gi(){ 8 ll a=0;char x=getchar();bool f=0; 9 while((x<‘0‘||x>‘9‘)&&x!=‘-‘)x=getchar(); 10 if(x==‘-‘)x=getchar(),f=1; 11 while(x>=‘0‘&&x<=‘9‘)a=a*10+x-48,x=getchar(); 12 return f?-a:a; 13 } 14 il ll gcd(ll a,ll b){return b?gcd(b,a%b):a;} 15 int main() 16 { 17 freopen("strongbox.in","r",stdin); 18 freopen("strongbox.out","w",stdout); 19 n=gi(),k=gi(); 20 for(int i=1;i<=k;i++)a[i]=gi(); 21 a[k]=gcd(a[k],n); 22 for(int i=1;i<k;i++)a[i]=gcd(a[k],a[i]); 23 for(ll i=1;i*i<=a[k];i++) 24 if(a[k]%i==0){ 25 q[++tot]=i; 26 if(i*i!=a[k])q[++tot]=a[k]/i; 27 } 28 sort(q+1,q+tot+1); 29 for(int i=1;i<k;i++)p[lower_bound(q+1,q+tot+1,a[i])-q]=1; 30 for(int i=1;i<=tot;i++) 31 if(p[i]) 32 for(int j=1;j<i;j++) 33 if(q[i]%q[j]==0)p[j]=1; 34 while(p[cnt])cnt++; 35 cout<<n/q[cnt]; 36 return 0; 37 }