1. 程式人生 > >[POI2011]SEJ-Strongbox

[POI2011]SEJ-Strongbox

color ret 又是 關系 tar strong http esp 非負數

題目大意:

一個有密碼箱,數字是0~n-1,其中有若幹個密碼,密碼的特點:若x是密碼,y是密碼,(x可以等於y)則(x+y)%n也是密碼。

給一個n(<=10^14),一個k(k<=min(250000,n)),給k個數(a[k]<n),前k-1個數不是密碼,第k個數是密碼。

求在0~n-1中,最多有多少個數字是密碼?

題解:

推薦(但是結論二的證明不太完整)

看起來和數論有一些關系。
而且一定是一個性質題。

結論1:若x是密碼,則gcd(n,x)是密碼

發現,x是密碼,則k*x%n都是密碼。

所以,一定存在一個t,c,使得t*x-n*c=gcd(n,x)

並且根據裴屬定理,不能用x湊出一個更小的密碼比gcd(n,x)更小,

結論2:若x,y是密碼,則gcd(x,y)是密碼。

根據裴屬定理,p*x+q*y=gcd(x,y)有整數解。

如果q是負數q=-q,那麽就是p*x+(c*n-q)*y=gcd(x,y)+c*n*y

那麽,就存在非負數p,q使得p*x+q*y=gcd(x,y) mod n

結論3:若x是所有密碼中最小的那一個,那麽,所有的密碼就是x,2x,3x,...kx,並且x是n的約數。

反證。設x是最小的,y是另一個密碼,若x不是y的約數,那麽gcd(x,y)<x,根據結論二,那麽gcd(x,y)就是一個更小的密碼。矛盾。

所以,任意的y都是x的倍數。

由於對於一個密碼z,根據結論1,gcd(n,z)也是密碼,所以,最小的密碼x是gcd(n,z)的約數,也就是n的約數。

所以,如果我們求出了滿足條件的x,那麽n/x就是答案。

我們密碼數量最多,所以,x必須取最小的。

由於給了一個a[k]是密碼,而x又是n的約數,所以x就一定是gcd(a[k],n)的約數。

並且,x不能是a[1~k-1]的約數,只要是,那麽x就能湊出ai,與ai不是密碼矛盾。

只要不存在這樣的ai,那麽x一定可以是最小的密碼(裴屬定理可以證明)。

所以,我們可以枚舉gcd(a[k],n)的約數,然後排序。

從小到大枚舉x,再暴力驗證是否是a[i]的約數,第一個符合的x,n/x就是答案。

代碼:

#include<bits/stdc++.h>
using namespace
std; typedef long long ll; const int N=250000+5; ll n,k; ll gcd(ll a,ll b){ return b?gcd(b,a%b):a; } ll a[N],fac[N]; int tot; int main(){ scanf("%lld%lld",&n,&k); for(int i=1;i<=k;i++)scanf("%lld",&a[i]); ll g=gcd(a[k],n); //cout<<"gg "<<g<<endl; for(ll i=1;i*i<=g;i++){ if(g%i==0){ fac[++tot]=i; if(i!=g/i) fac[++tot]=g/i; } }sort(fac+1,fac+tot+1); for(int i=1;i<=tot;i++){ //cout<<fac[i]<<" "; bool fl=true; for(int j=1;j<=k-1;j++){ if(a[j]%fac[i]==0){ fl=false;break; } } if(fl){ printf("%lld",n/fac[i]);return 0; } } //cout<<" over "<<endl; return 0; }

[POI2011]SEJ-Strongbox