2016多校联赛 hdu 5728 PowMod

8 篇文章 0 订阅
题意就是题目上的图的那个样子。给定的n是一个 n

 is a square-free number. 他是一个无平方因子数。也就是说他分解质因子后质因子的指数是1.


解决这个题分两步,先说第二步,第二步是 bzoj 3884原题,只是在这个题我们需要算底数是多少而已。


引用一下别人的证明(本人巨菜)

考虑欧拉定理,当 (a,p)= 时, aϕ(p)1(modp) 。 
而由此可以很容易得出一个结论: 
xϕ(p) 时,有

axaxmodϕ(p)+ϕ(p)(modp)

若令 f(p)=222...modp ,则 f(1)=0 。 
又由于是无穷的式子, 222... 的指数本来就是超过 ϕ(p) 的,所以我们可以改写成

f(p)=2(22...modϕ(p))+ϕ(p)modp=2f(ϕ(p))+ϕ(p)modp

因而得到了 f(p) 的递推式。

似乎这么计算是 O(p) 的,但是我们可以对 ϕ(ϕ(...ϕ(p))) 进行分析: 
p 为偶数,则 ϕ(p)p2
p 为奇数,则 p 存在一个奇数因子 q ,使得 ϕ(p) 存在一个偶数因子 (q1) ,转化为偶数的情况。 
由此可知, ϕ(ϕ(...ϕ(p))) 的计算经过 O(logp) 次的迭代就到了 1 ,所以 f(p) 的计算是 O(plogp) 的。



然后我们来到第一步:解决这个问题,我们需要知道几个性质

对于质数p,φ(p) = p - 1。注意φ(1)=1.

欧拉定理:对于互质的正整数a和n,有aφ(n) ≡ 1 mod n。

欧拉函数是积性函数——若m,n互质,φ(mn)=φ(m)φ(n)。

 若n是质数p的k次幂,φ(n)=p^k-p^(k-1)=(p-1)p^(k-1),因为除了p的倍数外,其他数都跟n互质。



因为此题时间的限制,我们只能筛选10^7以内的所有素数,在求素数的时候顺便就求出欧拉函数,再求出前n项和。

我们来看,如果m是10^7次方,n也是10^7(当然n不可能是10^7),那么我们就希望看能不能把这个i*n的数字变小,变小就有两个方法,把n变小,把m变小。


对于φ (i*n)来说,我们可以令n=x*pri,对于i*n的欧拉值就是求φ(i*x*pri,如果pri和i互质,就是积性的,就可以把pri提出去,φ(i*x)*(pri-1);如果i有因子p的话,我们就可以把m缩小,让m/p为上线, 不变n。依次递推。

当m等于1的时候,那么最终的值就是n的欧拉函数值,当n等于1的时候,就是前m个的欧拉函数值的和。


最方便的情况是先把n素因子分解,然后一个一个用就是咯。

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<map>
#include<cmath>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int maxn=1e7;
const int maxm=1e5*7;
ll prime[maxm+10];
bool isprime[maxn+10];
ll phi[maxn+10];
ll sphi[maxn+10];
ll index[50];
//ll sphi[maxn+10];
int cnt;
map<ll,ll>mp;
int res;
void init()
{
    cnt=0;
    phi[1]=1;
    sphi[1]=1;
    for(ll i=2; i<=maxn; i++)
    {
        if(!isprime[i])
        {
            prime[cnt++]=i;
            phi[i]=i-1;
        }
        for(ll j=0; j<cnt&&i*prime[j]<=maxn; j++)
        {
            isprime[i*prime[j]]=true;
            if(i%prime[j]==0)
            {
                phi[i*prime[j]]=phi[i]*prime[j];
                break;
            }
            else
                phi[i*prime[j]]=phi[i]*(prime[j]-1);
        }
        sphi[i]=(sphi[i-1]+phi[i])%mod;
    }
}
ll n,m;
ll p;
ll k;
ll mul(ll a,ll b,ll o)
{
    ll ans=1;
    while(b)
    {
        if(b&1)
            ans=(ans*a)%o;
        b>>=1;
        a=(a*a)%o;
    }
    return ans;
}
ll Phi(ll x)
{
    ll ret = x;
    for(ll i = 2; i * i <= x; ++i)
        if(x % i == 0)
        {
            ret -= ret / i;
            while(x % i == 0)
                x /= i;
        }
    if(x > 1)
        ret -= ret / x;
    return ret;
}
ll f(ll x)
{
    if(mp.count(x))
        return mp[x];
    ll c=phi[x];
    return mp[x]=(mul(k,f(c)+c,x))%x;
}
ll sovle(int id,ll n,ll m)
{
//    printf("%d %lld %lld\n",id,n,m);
//    getchar();

//    if(m==1)
//        return phi[n];
//    //printf("sssss");
    if(n==1)
        return sphi[m];
    if(m==0)
        return 0;
    if(id==-1)///没有什么用,因为测不合法的数据要停止工作,不爽
        return 0;
    return ((sovle(id-1,n/index[id],m)*(index[id]-1))%mod+sovle(id,n,m/index[id]))%mod;
}
void fen(ll n)
{
    //printf("%d\n",cnt);
    for(int i=0; i<cnt; i++)
    {
        if(!isprime[n])
        {
            index[res++]=n;
            break;
        }
        if(n%prime[i]==0)
        {
            index[res++]=prime[i];
            n/=prime[i];
        }
    }
}
int main()
{
    init();
    //printf("%d\n",cnt);
    //cout<<sphi[5]<<endl;
    while(~scanf("%lld%lld%lld",&n,&m,&p))
    {
        mp.clear();
        k=0;
        res=0;

        fen(n);
        res--;
        //printf("%d\n",res);
        k=(k+sovle(res,n,m))%mod;

       // printf("%lld\n",k);
        printf("%lld\n",f(p));
    }
}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值