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 注意通式(后面的函数就是通过通式写出来的)
解题思路:
看到这么一道题目后,首先做的是用数学的知识对式子进行化简,递推出同时因为Y=t*(x-1) (t为任意常数)。最后可得。两边同时取余得到%%。
接下来一步自己刚开始也没搞清楚为什么这么做:这里取t与a0的最大公约数gcd(t,a0),那么我们可以得到(x^n-1)*b%a0等价为(x^n-1)*(b/gcd)%(a0/gcd)因为(b/gcd)%(a0/gcd)两者互质,所以肯定不会为0了。对于%%=0来说,只能%=0。
设a'=,则%a'=0,即与1模a'同余。所以存在k的条件就是gcd(x,a')==1。同余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;
}