密码 [SEJ-Strongbox ]
题目链接:密码 [SEJ-Strongbox ]
(这是一道数论蓝题,想了好长时间啊~~)
【题目大意】
有一个密码箱,0到n-1中的某些整数是它的密码。且满足:如果a和b都是它的密码,那么(a+b)%n也是它的密码(a,b可以相等,%表示整除取余数),某人试了k次密码,前k-1次都失败了,最后一次成功了。
问:该密码箱最多有多少不同的密码。
【样例】
输入:
42 5
28 31 10 38 24
输出:
14
【题解】:
首先从题目中的出的两个结论:
结论一:
如果x为密码,那么 g c d ( x , n ) gcd(x,n) gcd(x,n)也是密码。
证明:
因为x是密码,由题意得2x%n,3x%n……kx%n都是密码。
由于不定方程 a x + b y = c ax+by=c ax+by=c有整数解的充要条件是 g c d ( a , b ) ∣ c gcd(a,b)|c gcd(a,b)∣c,所以 x k + n c = g c d ( x , n ) xk+nc=gcd(x,n) xk+nc=gcd(x,n)一定有整数解,那么 ? k ∈ Z ?k∈Z ?k∈Z使 k x ≡ g c d ( x , y ) ( m o d n ) kx ≡ gcd(x,y)(mod n) kx≡gcd(x,y)(modn),故gcd(x,n)也是密码。
结论二:
如果x,y为密码,那么 g c d ( x , y ) gcd(x,y) gcd(x,y)也是密码。
证明:
由题意得:以为x,y是密码,显然(px+qy)% n也是密码,因为 x p + y q = g c d ( x , y ) xp+yq=gcd(x,y) xp+yq=gcd(x,y)一定有整数解,那么 ? p , q ∈ Z ? p,q∈Z ?p,q∈Z使得 p x + q y ≡ d ( m o d n ) px+qy≡d(mod n) px+qy≡d(modn),故 g c d ( x , y ) gcd(x,y) gcd(x,y)也是密码。
再看看这道题,设 x , y x,y x,y 均为密码且x是所有密码中最小的,若 x ? y x?y x?y,则 g c d ( x , y ) < x gcd(x,y)<x gcd(x,y)<x且 gcd(x,y) 是密码(结论二),与假设矛盾,故 x ∣ y x|y x∣y。因此,这组密码为 x , 2 x , 3 x , ? , k x ( k x < n ) x,2x,3x,?,kx (kx<n) x,2x,3x,?,kx(kx<n)
设 d = g c d ( m k , n ) d=gcd(mk,n) d=gcd(mk,n),由于 m k mk mk是密码,根据结论一,d 也是密码,又由①得: x ∣ d x|d x∣d(仍设x是所有密码中最小的那个)
但是这道题给了一些限制条件:
- 要求最多有多少密码,故我们希望x尽量的小,密码数量为n/x
- 给出了k?1个不是密码的数字,故 x 不能整除 m1,m2,?,mk?1
综上所述,这道题的实现方法是:在根号的时间里暴力枚举处理出 gcd(mk,n) 的所有因数,去除能整除 m1,m2,?,mk?1 中任意一个的数,设剩下的数中最小的为x,则答案就是 n/x
AC代码:
#include<bits/stdc++.h>
#define LL long long
using namespace std;
const int sea=250010;
LL a[sea],q[sea],f[sea],n,k,tot=0,ans;
LL gcd(LL a,LL b){
if(a<b) swap(a,b); return b?gcd(b,a%b):a;}
int main()
{
scanf("%lld%lld",&n,&k);for(int i=1;i<=k;i++) scanf("%lld",&a[i]);a[k]=gcd(a[k],n);for(int i=1;i<k;i++) a[i]=gcd(a[i],a[k]);for(LL i=1;i*i<=a[k];i++)if(a[k]%i==0) {
q[++tot]=i;if(i!=a[k]/i) q[++tot]=a[k]/i;}sort(q+1,q+tot+1);for(int i=1;i<k;i++)f[lower_bound(q+1,q+tot+1,a[i])-q]=1;for(int i=1;i<=tot;i++)if(f[i])for(int j=1;j<i;j++)if(q[i]%q[j]==0) f[j]=1;ans=1; while(f[ans]) ans++;printf("%lld\n",n/q[ans]);return 0;
}