HDU 3307

不得不说。。这道题让我有一种回到高三的感觉。。完全就是一道数学题。。不过确实很不错。。非常活

题目大意

已知 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;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值