【hdu-3307】Description has only two Sentences (欧拉函数)

a n = X*a n-1 + Y and Y mod (X-1) = 0. 
Your task is to calculate the smallest positive integer k that a k mod a 0 = 0. 

Input

Each line will contain only three integers X, Y, a 0 ( 1 < X < 2 31, 0 <= Y < 2 63, 0 < a 0 < 231).

Output

For each case, output the answer in one line, if there is no such k, output "Impossible!".

Sample Input

2 0 9

Sample Output

1

做这道题时参考过的博客:(在这里感谢这些博主,谢谢大佬们的文章)

化简步骤:

https://blog.csdn.net/wust_cyl/article/details/77431451     

https://www.cnblogs.com/liyinggang/p/5535925.html

https://blog.csdn.net/z690933166/article/details/11830999

欧拉函数的模板:

https://www.cnblogs.com/handsomecui/p/4755455.html

https://blog.csdn.net/sentimental_dog/article/details/52002608

欧拉函数:

https://blog.csdn.net/qq_36409190/article/details/53173777      注意通式(后面的函数就是通过通式写出来的)

解题思路:

看到这么一道题目后,首先做的是用数学的知识对式子进行化简,递推出a_n=x^n*a_0+Y*(1+......+x^{n-1})=x^n*a_0+Y*(x^{n}-1)/(x-1)。同时因为Y=t*(x-1) (t为任意常数)。最后可得a_n=x^n*a_0+t*(x^{n}-1)。。两边同时取余得到a_n%a_0=(x^n-1)*t%a_0

接下来一步自己刚开始也没搞清楚为什么这么做:这里取t与a0的最大公约数gcd(t,a0),那么我们可以得到(x^n-1)*b%a0等价为(x^n-1)*(b/gcd)%(a0/gcd)因为(b/gcd)%(a0/gcd)两者互质,所以肯定不会为0了。对于a_k%a_0=(x^n-1)*(t/gcd(y,a_0))%(a_0/gcd(y,a_0))=0来说,只能(x^n-1)%(a_0/gcd(y,a_0))=0。

设a'=(a_0/gcd(y,a_0)),则(x^n-1)%a'=0,即x^n与1模a'同余。所以存在k的条件就是gcd(x,a')==1。x^k同余1(mod a')。

接下来,通过欧拉定理,(也称费马-欧拉定理)是一个关于同余的性质。欧拉定理表明,若n,a为正整数,且n,a互质,则:,在这里a指的是本题的x。n指的是本题的a'。所以我们要求的k就是欧拉定理中的欧拉函数φ(n)即φ(a')。但是这里求出的欧拉函数不一定是最小的解,所以还需要到它的因子里面去找。所以接下来要分解质因子,找出最小的符合条件的数。

疑问:

1、为什么要求出最大公约数之后再进行求解?是不是形如(xn-1)*y%a=0的式子可以都可以变成 (xn-1)%(a/gcd(y,a))=0进行求解?

2、那两个式子为什么等价。(看着别人的博客中提出了这些问题,其实我自己也有疑问)

为什么除gcd,为什么能除gcd。

(刚开始想法:这里除最大公约数是不是为了使得两者互质,然后使得方程有解?后来发现好像不是这样)

解答:(其实上面的问题很好解释,因为这些过程都是推导出来的)

(x^n-1)Y = 0(mod a0)
a0 * x = (x^n-1)*Y
a0 * x / gcd = (x^n-1) * Y / gcd
(x^n-1)(Y/gcd) = 0(mod a0 / gcd)

这里要感谢hyf大佬的帮助

求欧拉函数的部分:(定义:欧拉函数是小于n的正整数中与n互质的数的数目)

typedef long long ll;
ll e[100][2];//e[][0]存因子,,e[][1]存这个因子有多少个 
ll Euler(ll n)//直接求小于或等于n,且与n互质的个数:(模板) 由φ(x)的通式得出 
{
    ll ret=n; 
    for(ll i=2;i*i<=n;i++)
     if(n%i==0)
      {
        ret=ret/i*(i-1);//先进行除法防止溢出(ret=ret*(1-1/p(i)))
        while(n%i==0)
		n/=i;
     }
    if(n>1)
          ret=ret/n*(n-1);
        return ret;
}

这部分是有欧拉函数的值,求法的原理其实在于欧拉函数的通式:φ(x) = x ( 1 - 1/p1 )( 1 - 1/p2 )( 1 - 1/p3 )( 1 - 1/p4 )…..( 1 - 1/pn )。其中p1, p2……pn为x的所有质因数,x是不为0的整数。每一个小括号内都可以化成((pi-1)/pi)。代码中的ret就是通式中的x,接下来遍历所有数,当这个数是x的质因子时,就进行通式中的操作,并让ret不断除这个质因子,直到这个数不再是他的因子时。(后面含有这个因子的合数不再会被选中,也可能到不了那里)。这里要注意注释的那句话:先进行除法防止溢出(ret=ret*(1-1/p(i)))。最后的这一步判断,是看看这个剩下的数是什么,如果是1,说明所有的质因子都被到了,若不是1,那很可能是一个素数,这个数没有进行上面操作,所以要在进行一次操作。

疑问:这个地方为什么是i*i??(这里我试过改成i<=sqrt(n)也是对的)。

解答:

分解质因子的部分:

这里开的二维数组一维用来存放有哪些质因子,另一维存放同列的质因子在这个数中有几个,就是说这个数可以整除这个质因子多少次。循环内部就是找到质因数并且判断出他的个数,最后的if是为了判断剩下的数是什么,和上一个函数基本一样。

ll e[100][2];//e[][0]存因子,,e[][1]存这个因子有多少个 
void ff(ll ans,int &id)//分解质因数 
{
	for(ll i=2;i*i<=ans;i++)
	{
		if(ans%i==0)
		{
			e[id][0]=i;
			e[id][1]=0; 
			while(ans%i==0) ans/=i,e[id][1]++;
			id++; 
		}
	 } 
	 if(ans>1)
	 {
	 	e[id][0]=ans;
	 	e[id++][1]=1;
	 }
} 

疑问:这里开的数组的大小怎么判断?自己想法:放素数,80000大的数组 应该可以盛1e6(可能有偏差)。所以这里应该不用开太大。

主函数:

int main()
{
	ll x,y,a0;
	while(scanf("%lld%lld%lld",&x,&y,&a0)!=EOF)
	{
		 y=y/(x-1);
		ll g=gcd(y,a0); 
		 a0=a0/g;
		if(gcd(x,a0)!=1)
		{
			printf("Impossible!\n");
		} 
		else
		{
			ll ans=Euler(a0);
			int id=0;
			ff(ans,id);
			for(int i=0;i<id;i++)
			{
				for(int j=0;j<e[i][1];j++) 
				{
					if(qsm(x,ans/e[i][0],a0)==1) ans/=e[i][0];
				}
			} 
			printf("%lld\n",ans);
		}
	}
	return 0;
 } 

主函数还比较好看,主要解释一下else中的操作。先求出欧拉函数,然后设置一个变量,来存有多少个质因数,接下来的两层循环是为了找到最小的符合条件的数。if中的判断就是看少了这的质因数后(这里的数比欧拉函数要小),能否满足k的条件。第一层循环遍历所有的质因数,第二层循环,遍历这个质因数有几个。如果两层循环都遍历完并且if语句都进去那么ans应该为1。但是大部分情况是不可能所有的if都进去。这里因为第二层中间的j写成了i 而超时了好几次,找了好几个小时,下次一定要注意。

代码:

#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<cstring> 
#include<string.h>
using namespace std;
typedef long long ll;
ll e[100][2];// e[][0]存因子,e[][1]存这个因子有多少个 
ll Euler(ll n)//直接求小于或等于n,且与n互质的个数:(模板)  由φ(x)的通式得出 
{
    ll ret=n;//注意long long 
    for(ll i=2;i*i<=n;i++)//两个相乘??开根号 
     if(n%i==0)
      {
        ret=ret/i*(i-1);//先进行除法防止溢出(ret=ret*(1-1/p(i)))
        while(n%i==0) 
        n/=i;
     }
    if(n>1)
          ret=ret/n*(n-1);
        return ret;
}
ll gcd(ll a,ll b)
{
	return b==0?a:gcd(b,a%b);
}
ll qsm(ll a,ll b,ll mod)
{
	ll t=1;
	while(b)
	{
		if(b&1)	t=t*a%mod;
		a=a*a%mod;//??? 
		b>>=1;
	}
	return t;
}
void ff(ll ans,int &id)//分解质因数 
{
	for(ll i=2;i*i<=ans;i++)
	{
		if(ans%i==0)
		{
			e[id][0]=i;
			e[id][1]=0;//注意等于0 
			while(ans%i==0) ans/=i,e[id][1]++;//一条语句
			id++; 
		}
	 } 
	 if(ans>1)
	 {
	 	e[id][0]=ans;
	 	e[id++][1]=1;
	 }
} 

int main()
{
	ll x,y,a0;
	while(scanf("%lld%lld%lld",&x,&y,&a0)!=EOF)
	{
		 y=y/(x-1);
		ll g=gcd(y,a0); 
		 a0=a0/g;
		if(gcd(x,a0)!=1)
		{
			printf("Impossible!\n");
		} 
		else
		{
			ll ans=Euler(a0);
			int id=0;
			ff(ans,id);
			for(int i=0;i<id;i++)
			{
				for(int j=0;j<e[i][1];j++)//注意j从哪开始从1有=从0没= //注意是j不是i,注意这个细节。 
				{
					if(qsm(x,ans/e[i][0],a0)==1) ans/=e[i][0];
				}
			} 
			printf("%lld\n",ans);
		}
	}
	return 0;
 } 

猜你喜欢

转载自blog.csdn.net/QQ_774682/article/details/81204861