不得不说。。这道题让我有一种回到高三的感觉。。完全就是一道数学题。。不过确实很不错。。非常活
题目大意
已知 x y a0 ,an = x*an-1 + y 且 y%(x-1) ==0 问最小的k 使得 ak%a0 ==0
先提一下一个欧拉函数性质 即 当 a n 互质时 a ^ Φ( n )= 1 % n
那么 k = Φ ( a0 ),
乍一看这题思路有点闭塞,我们先去分析递推式
an = x* an-1 + y 如果对数列有一些敏感的话 我们可以看出 an + t 可以是一个等比数列 公比为 x
那么经计算 t = y / (x-1) 可知 t 是一个已知的常数 故此结论成立
an + t = ( a0 + t ) * x ^ n;
则 ak = x^n * a0 + ( x^n - 1) * t = k0 * a0 ( k0 = ak / a0 )
对两边同时 对 a0 取余
0 = 0 + ( x^n - 1 ) * t % a0
则有 ( x^n -1 ) * t % a0 = 0 之后我认为最神奇的一步 来了
首先 假设 t % a0 == 0 等价于 ( t / gcd(t,a0) ) % ( a0 / gcd(t,a0) ) 等价于 t' % a0' == 0 ( 除法操作完之后)
那么 t' 和 a0' 互质
则有 x^n %a0 = 1 % a0 符合公式了
但是 问题来了, 根据抽屉原理 x^k % a0 == 1 可能直接求出的 k 不是我们想要的最小 因为会有不止一个 k 使得 x^k % a0 == 1
我们要找到最小的那个 k
那么 我们就需要对 k 进行一波 因式分解,求出最小的那组分解数 来求出最小的 k
这是我找到的一个分解质因数的非常好用的函数
ll ed[maxn][2];
//ed[n][0] 分解数 ed[n][1]能分解成几个
void divide(ll ans ,ll &id)
{
for(ll i=2;i*i<=ans;i++)
{
if(ans%i==0)
{
ed[id][0]=i;
ed[id][1]=0;
while(ans%i==0)
{
ans/=i;
ed[id][1]++;
}
id++;
}
}
if(ans>1)
{
ed[id][0]=ans;
ed[id++][1]=1;
}
}
分解完成后 我们再不断从大到小遍历寻找一遍 就得出答案 大功告成了
以下为 AC代码
#include<cmath>
#include<cstdio>
#include<iostream>
using namespace std;
#define ll long long int
const int maxn = 100;
ll phi(ll x)
{
ll ans = x;
for(ll i=2;i*i<=x;i++)
{
if(x%i==0)
{
ans=ans/i*(i-1);
while(x%i==0)
x/=i;
}
}
if(x>1)
ans = ans/x*(x-1);
return ans;
}
ll ed[maxn][2];
//ed[n][0] 分解数 ed[n][1]能分解成几个
ll gcd(ll a, ll b)
{
return b==0 ? a:gcd(b,a%b);
}
ll pow(ll a,int n,ll mod)
{
ll ans=1;
while(n)
{
if(n&1)
ans=ans*a%mod;
a=a*a%mod;
n>>=1;
}
return ans;
}
void divide(ll ans ,ll &id)
{
for(ll i=2;i*i<=ans;i++)
{
if(ans%i==0)
{
ed[id][0]=i;
ed[id][1]=0;
while(ans%i==0)
{
ans/=i;
ed[id][1]++;
}
id++;
}
}
if(ans>1)
{
ed[id][0]=ans;
ed[id++][1]=1;
}
}
int main()
{
ll x,y,a0;
while(cin>>x>>y>>a0)
{
y = y / (x-1);
ll d = gcd(y,a0);
a0 /= d;
if(gcd(x,a0)!=1) //欧拉公式成立不了
{
cout<<"Impossible!"<<endl;
}
else
{
ll ans = phi(a0);
ll id = 0;
divide(ans,id);
for(int i=0;i<id;i++)
{
for(int j=0;j<ed[i][1];j++)
{
if(pow(x,ans/ed[i][0],a0)==1)
ans /= ed[i][0];
}
}
cout<<ans<<endl;
}
}
return 0;
}