BZOJ2277: [Poi2011]Strongbox
n<=10^14,0~n-1中有一些数是密码,且满足:a是密码,b是密码,那么(a+b)%n也是密码(a,b可相等),现小明试了m<=250000个数,前面都错,最后一个对,问n个数中最多有多少密码。
好端端的一道数论题变卡常题。。
根据那个规则,把最后猜到那个数字t带进去,那么t,2t,3t……都是密码,然而是%n下,所以0,(t,n),2*(t,n),……,n-(t,n)都是密码。
那假如有别的数字是密码,那肯定也类似这样构成一个等间隔的东西。所以目标是找到这个最小的间隔。
这个间隔应该能达到(t,n),也就是应该是(t,n)的因子,所以现在就可枚举(t,n)的因子检验一下其他不满足的位置会不会被搞到,也就是有没有x|Ai即可。
然而因子个数*m爆炸了,于是花了一下午想有什么其他算法。。然而这是对的,只需要卡一卡即可
因为10^14以内的数字最多只有17000多个因子,而对一个数Ai,其实检验(Ai,n)是否满足不被x整除也是一样的,因为Ai的其他因子在这个周期跳跃活动中没有啥用。于是把m个数都对n取gcd,然后去个重,复杂度就变成因子个数^2+mlogn了!
哦。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstring> 2 #include<cstdlib> 3 #include<cstdio> 4 //#include<assert.h> 5 #include<algorithm> 6 //#include<iostream> 7 using namespace std; 8 9 #define LL long long 10 #define maxn 250011 11 LL n; int m; LL a[maxn]; 12 LL gcd(LL a,LL b) {return b?gcd(b,a%b):a;} 13 bool check(LL x) 14 { 15 for (int i=1;i<=m;i++) 16 if (a[i]%x==0) return 0; 17 return 1; 18 } 19 int main() 20 { 21 scanf("%lld%d\n",&n,&m); 22 for (int i=1;i<=m;i++) scanf("%lld",&a[i]),a[i]=gcd(a[i],n); 23 LL base=a[m]; sort(a+1,a+m); m=unique(a+1,a+m)-a-1; 24 LL ans; 25 for (int i=1;1ll*i*i<=base;i++) if (base%i==0) 26 { 27 if (check(i)) {ans=n/i; break;} 28 else if (check(base/i)) {ans=n/(base/i);} 29 } 30 printf("%lld\n",ans); 31 return 0; 32 }