标签:scanf 另一个 .net color 排序 long main ons 必须
题目大意:
一个有密码箱,数字是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; }
标签:scanf 另一个 .net color 排序 long main ons 必须
原文地址:https://www.cnblogs.com/Miracevin/p/9777115.html