ZJOI模拟 幂【CRT+Pollard-Rho】

题目大意:

给定正整数 n n ,求最小的正整数k满足对于任意正整数 a a ,anka(modn),无解则输出-1。 n1018 n ≤ 10 18

解题思路:

首先如果 n n 有平方因子p2,那么令 a=p a = p ,则 pnkmodn p n k mod n 一定是 p2 p 2 的倍数,所以无解。
否则根据CRT得到 n n 的质因子个同余方程: anka(modp)
p=2 p = 2 则一定成立,否则有 nk1(modp1) n k ≡ 1 ( mod p − 1 ) 。首先要满足 n,p1 n , p − 1 互质,然后类似于求原根,根据欧拉定理,得知 k k 一定为ϕ(p1)的因数。
由于如果 nx1 n x ≠ 1 ,那么 x x 的因数肯定也不行,所以求出ϕ(p1)我们不用枚举因数,用试除法即可。

最后的答案为所有 k k <script type="math/tex" id="MathJax-Element-1568">k</script>的lcm。

#include<iostream>
#include<algorithm>
#include<map>
#define ll long long
#define LL __int128
using namespace std;
ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
ll Pow(ll x,ll y,ll mod)
{
    x%=mod;ll res=1;
    for(;y;y>>=1,x=(LL)x*x%mod)
        if(y&1)res=(LL)res*x%mod;
    return res;
}
bool check(ll x,ll n,ll r,ll s)
{
    x=Pow(x,r,n);ll pre=x;
    for(int i=1;i<=s;i++)
    {
        x=(LL)x*x%n;
        if(x==1&&pre!=1&&pre!=n-1)return false;
        pre=x;
    }
    return x==1;
}
bool MR(ll n)
{
    if(n<2)return false;
    if(n==2)return true;
    if(!(n&1))return false;
    ll r=n-1,s=0;
    while(!(r&1))r>>=1,s++;
    for(int i=0;i<9;i++)if(!check(rand()%(n-1)+1,n,r,s))return false;
    return true;
}
ll rho(ll n,ll c)
{
    ll x=rand()%n,y=x,k=2,p=1;
    for(int i=1;p==1;i++)
    {
        x=((LL)x*x+c)%n;
        p=x>y?x-y:y-x;
        p=gcd(p,n);
        if(i==k)y=x,k<<=1;
    }
    return p;
}
void div(map<ll,int>&D,ll n)
{
    if(n==1)return;
    if(MR(n)){D[n]++;return;}
    ll t=n;
    while(t==n)t=rho(n,rand()%(n-1)+1);
    div(D,n/t),div(D,t);
}
ll Phi(ll n)
{
    ll res=n;
    map<ll,int>D;div(D,n);
    for(auto &v:D)res=res/v.first*(v.first-1);
    return res;
}
ll solve(ll n,ll p)
{
    if(gcd(n,p)!=1){puts("-1");exit(0);}
    ll res=Phi(p);
    map<ll,int>D;div(D,res);
    for(auto &v:D)
        for(int i=1;i<=v.second;res/=v.first,i++)
            if(Pow(n,res/v.first,p)!=1)break;
    return res;
}
int main()
{
    freopen("pow.in","r",stdin);
    freopen("pow.out","w",stdout);
    ll n;map<ll,int>D;
    cin>>n;div(D,n);
    ll ans=1;
    for(auto &v:D)
    {
        if(v.second>1){puts("-1");return 0;}
        if(v.first!=2)ans=lcm(ans,solve(n,v.first-1));
    }
    cout<<ans<<'\n';
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值