1. 程式人生 > >[poi2011]bzoj 2277 —— strongbox·[洛谷3518]

[poi2011]bzoj 2277 —— strongbox·[洛谷3518]

line 一行 while != 容易 pre 新的 name gpo

·問題描述·

  有一個密碼箱,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 }

[poi2011]bzoj 2277 —— strongbox·[洛谷3518]