【数论】【欧拉函数】【快速幂】洗牌机

3333: 洗牌机

时间限制: 2 Sec  内存限制: 512 MB

题目描述

2n张牌,放在2n个从12n的有序位置上。洗牌机每次可以把第i张牌洗到pi)的位置上。Pi)的定义如下:

 

问经过最少多少轮洗牌,才会使所有牌回到原来的位置。

输入

输入格式:

有多组数据,每组数据一个整数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

原文地址:https://www.cnblogs.com/LinqiongTaoist/p/7203738.html