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了!

哦。

 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 }
View Code

 

posted @ 2017-12-31 19:49  Blue233333  阅读(253)  评论(0编辑  收藏  举报