3333: 洗牌机
时间限制: 2 Sec 内存限制: 512 MB题目描述
有2n张牌,放在2n个从1到2n的有序位置上。洗牌机每次可以把第i张牌洗到p(i)的位置上。P(i)的定义如下:
问经过最少多少轮洗牌,才会使所有牌回到原来的位置。
输入
输入格式:
有多组数据,每组数据一个整数n(n<=109)
输出
输出格式:
对于每组数据,输出一个整数,表示答案。
样例输入
(如果复制到控制台无换行,可以先粘贴到文本编辑器,再复制)
1
样例输出
2
提示
来源
#----------------------------------------------------------------------------------------------#
首先,理解这道题我都觉得要疯掉了,什么叫“回到原来的位置”,我连牌都不知道在哪儿,怎么知道它有没有“回到原来的位置”,现在解释一下:回到原来的位置就相当于第1张牌回到了第1个位置。
极其恶心的题,如果没有“多组数据”,那我们都好说,枚举嘛,枚举第1张牌就可以了:
#include<cstdio> int n; int p(int x) { if(x<=n) return 2*x; return 2*(x-n)-1;//它的要求就这样 } int main() { while(~scanf("%d",&n)) { int x=1,ans=0; do//如果不用do-while会直接输出ans,因为x已经赋为1了 { x=p(x); ans++; }while(x!=1);//有没有回到1 printf("%d ",ans); } }
不难理解……也不难超时……
正解是怎样的呢?以下便是推理过程了(尽管辣眼睛,但我们不畏艰险,还是得慢慢推):
①P函数是分段的,显然很麻烦,所以我们机智地把它变为了一个公式:return 2*i%(2*n+1),也就是说:P(i)=2*i%(2*n+1)。
②做一次P(i)是2*i%(2*n+1),那么做多次呢?不难得到:Pk(i)=(2^k)*i%(2*n+1),k为次数。
③所以,按照枚举的方法,将i假设为1,方便计算(不会影响结果,方程两边同时乘i可以还原),也就是说,要求k为几时,(2^k)%(2*n+1)为1,就是说又回到了原位。
④这一步很关键,我们知道欧拉定理:若m>1,(a,m)=1(a,m互质),则a^φ(m)≡1 (mod m),φ(m)还是解释一下,指0-m的整数中与m互质的数的个数,关于证明什么的自己网上搜吧。所以这里就知道了:2和(2*n+1)是互质的(一个奇数一个偶数),所以2^φ(2*n+1)≡1
(mod
2*n+1)。
⑤求出φ(2*n+1),然后没完。φ(2*n+1)虽然一定能使原方程成立,但它并不一定是最小的……所以,我们还要枚举φ(2*n+1)的因数(因为它的因数一定也能使原方程成立)。
⑥2的乘方要用快速幂哦,每次%(2*n+1)。
代码:
#include<cstdio> #include<cmath> long long n;//被int坑了,所以直接ctrl+f全换long long long long skm(long long x,long long n,long long mod)//快速幂 { long long t=1; while(n) { if(n&1) t=(t*x)%mod; n>>=1; x=x*x%mod; } return t; } long long phi(long long x)//求φ { long long m=(long long)sqrt(x+0.5); long long sum=x; for(long long i=2;i<=m;i++) if(x%i==0) { sum=sum/i*(i-1); while(x%i==0) x/=i; } return x>1?sum/x*(x-1):sum; } int main() { while(~scanf("%lld",&n)) { long long ph=phi(2*n+1); long long ans=ph; for(long long i=2;i*i<=ph;i++)//枚举到sqrt(ph) if(ph%i==0)//如果是因数 { if(skm(2,i,2*n+1)==1)//i为ph因子 if(i<=ans) { ans=i; break;//这里i从小到大枚举,所以只要符合条件就一定为最小了 } if(skm(2,ph/i,2*n+1)==1)//ph/i也是ph的因子 if(ph/i<=ans)//暂时还不能确定ph/i的大小,所以用ans先比较一下 ans=ph/i; } printf("%lld ",ans); } }
By WZY