HDU - 4335 What is N? 欧拉降幂 + 循环节

传送门

题意:给定p, b(0<=b<p<=10^5)和m(1<=m<2^64),问有多少个n满足n^(n!)=b (mod p)。

思路:首先要知道这个结论:

当n!<Phi(C)时,此时我们暴力解决就可。

当n!大于phi(P)的时候,就需要用上面的降幂公式了。

方法还是暴力,n!%phi(p)会出现0,这是必然的,至少n>=phi(p)为0,

那么(n+1)!%phi(p)也为0,这便出现了重复,转变为n^(phi(p))%p==b的问题了。

固定了指数,根据鸽巢原理,余数是循环的,那么只要找出p个的结果,之后通过循环节求解便可以了。

Trick:当P为1的时候,b为0,这时候答案是m+1,不过m可能为2^64-1,如果加1的话就会溢出。

#include<bits/stdc++.h>
#define ll unsigned long long
using namespace std;
const int maxn=1e5+50;
ll arr[maxn];
ll eular(ll n)
{
    ll res=n;
    for(ll i=2;i*i<=n;i++)
    {
        if(n%i==0)
        {
            res=res-res/i;
            while(n%i==0)
            {
                n/=i;
            }
        }
    }
    if(n>1)
    {
        res=res-res/n;
    }
    return res;
}
ll power(ll a,ll b,ll mod)
{
    ll ans=1;
    while(b)
    {
        if(b&1)
            ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}
int main()
{
    int t;
    scanf("%d",&t);
    int cas=0;
    while(t--)
    {
        ll b,p,m;
        scanf("%llu%llu%llu",&b,&p,&m);
        printf("Case #%d: ",++cas);
        if(p==1)
        {
            if(m==18446744073709551615ll)
                printf("18446744073709551616\n");
            else
                printf("%llu\n",m+1);
            continue;
        }
        ll ph=eular(p);
        ll fac=1;
        ll i;
        ll ans=0;
        for(i=0;i<=m&&fac<ph;i++)
        {
            if(power(i,fac,p)==b)
            ans++;
            fac=fac*(i+1);
        }
        fac%=ph;
        for(;i<=m&&fac;i++)
        {
            if(power(i,fac+ph,p)==b)
            {
                ans++;
            }
            fac=(fac*(i+1))%ph;
        }
        if(i<=m)
        {
            ll cnt=0;
            for(ll j=0;j<p;j++)
            {
                arr[j]=power(i+j,ph,p);
                if(1ll*power(i+j,ph,p)==b)
                {
                    cnt++;
                }
            }
            ll num=(m-i+1)/p;// 循环节长度 
            ans+=num*cnt;
            num=(m-i+1)%p;// 剩余部分 
            for(ll j=0;j<num;j++)// 这里是开区间 因为num等于0时说明正好能整除 故不进行操作
            {
                if(arr[j]==b)
                {
                    ans++;
                }
            }
        }
        printf("%llu\n",ans);
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值