【HDOJ 5728】PowMod 欧拉定理+递归
作者:互联网
参考 https://blog.csdn.net/wust_zzwh/article/details/51966450
题目链接
https://acm.hdu.edu.cn/showproblem.php?pid=5728
思路
本题的过程分为两部分:求k,和求k无限次方对p取模。
求k
经分析知,这是无论如何不能直接暴力求解的
假设p为n的因子之一(由题意知,p的个数肯定是1个)
第一行:分类可以得到
第二行:左边,提出公共因子\(\phi[p]\);右边,容易发现n里面必然存在p这个因子,由欧拉函数性质,\(\phi[p*p]=p(p-1)\),可以直接提出p来
第三行:对于质数p,\(\phi[p]+1=p\)
第四行:展开括号
然后发现前面两项可以合并
原式可以写成 $ \ k=f(n,m)= \phi[p]*f(n/p,m)+f(n,m/p) \ \ $ ,就可求出k
k的无限次方
欧拉定理内容:
\(当b>\phi[m]时\),
$ a^b \ mod \ p = a^{b \ mod \ \phi[m]+\phi[m] } \ mod \ p $
根据欧拉定理,可以对指数进行降幂
而又可以递归地求出指数:只需每次把 m 变成 \(\phi[m]\)
code
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N = 10000005,mod = 1e9+7;
int t;
int n,m,pp,nn;
bool vis[N];
int cnt;
int p[N],phi[N];
ll pre[N];
int pcnt,prime[N];
void phi_init(int N){
memset(vis,0,sizeof(vis));
cnt=0;
pre[1]=1;
for(int i=2;i<=N;i++){
if(!vis[i]) p[++cnt]=i,phi[i]=i-1;
for(int j=1;j<=cnt&&i*p[j]<=N;j++){
vis[i*p[j]]=1;
if(i%p[j]==0){
phi[i*p[j]]=p[j]*phi[i];
break;
}
phi[i*p[j]]=(p[j]-1)*phi[i]; //Euler函数是积性函数
}
pre[i]=(pre[i-1]+phi[i])%mod;
}
}
void func(int n){
pcnt=0;
ll N=n;
for(int i=1;p[i]*p[i]<=N;i++){
if(p[i]>n) break;
if(n%p[i]==0){
prime[++pcnt]=p[i];
n/=p[i];
}
}
if(n>1) prime[++pcnt]=n;
}
ll f(int id,int n,int m){
if(m==0) return 0;
if(n==1) return pre[m];
int p=prime[id];
return (phi[p]*f(id-1,n/p,m)%mod+f(id,n,m/p))%mod;
}
ll qPow(ll a,ll b,ll mod){
ll ans=1;
while(b){
if(b%2){
ans*=a;
ans%=mod;
}
a*=a;
a%=mod; //注意这里也需要取模
b/=2;
}
return ans+mod;
}
ll solve(ll base,ll mod){
if(mod==1) return 1;
ll up=solve(base,phi[mod]);
return qPow(base,up,mod);
}
int main(){
phi_init(1e7);
while(scanf("%d%d%d",&n,&m,&pp)==3){
func(n);
ll k=f(pcnt,n,m);
printf("%lld\n",solve(k,pp)%pp);
}
system("pause");
return 0;
}
标签:PowMod,5728,return,int,ll,phi,pcnt,HDOJ,mod 来源: https://www.cnblogs.com/re0acm/p/16508142.html