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

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章