[Description]求值
[Solution]
不要被無限個2嚇到了,這一題有一些有趣的性質可以發掘的。
這裏介紹兩個解法。
· Solution 1
我們溫習一下歐拉定理:
和它的推廣:
我們發現,這題的n,p並不一定互素啊,怎麼辦呢?我們可以讓他們強行互素。
利用公式:
我們把原題中的p分爲2^k+y
所以原式化爲
此時y是奇數,和指數互質了!然後就可以愉快地使用歐拉定理–原式化爲
我們發現中間的指數一部分又與原問題相似,於是想到可以遞歸求解。
那邊界是什麼呢?我們發現,phi(y)會不斷縮小,而且每次至少會除去一個2,所以遞歸的深度最多爲log2(p),當y=1時,返回0即可。
需要事先篩好phi值或者直接需要的時候根號時間計算求解。
複雜度O(p+log2(p))–線性篩/O(log2(p)*sqrt(p))–直接計算。
實踐過程中第二種方法遠遠快於第一種。
· Solution 2
還是根據公式
設答案爲f(p),有
同樣遞歸求解即可,複雜度同第一個解。
[Code]
給出兩種解法的代碼,第一種用的線性篩,第二種直接求解。
· Code 1
#include<cstdio>
typedef long long ll;
const int maxn=10000001;
int phi[maxn]={0,1};
void MakePhiList(){
for(int i=2;i<maxn;i++) if(!phi[i])
for(int j=i;j<maxn;j+=i){
if(!phi[j]) phi[j]=j;
phi[j]=phi[j]/i*(i-1);
}
}
ll pow(ll a,int n,int p){
ll ans=1;
while(n) {
if(n&1) ans=ans*a%p;
a=a*a%p; n>>=1;
}
return ans;
}
ll f(int x){
if(x==1) return 0;
int k=0; while(!(x%2)) x/=2,k++;
return pow(2,(f(phi[x])%phi[x]-k%phi[x]+phi[x])%phi[x]+phi[x],x)<<k;
}
int main(){
MakePhiList();
int kase; scanf("%d",&kase);
while(kase--){
int x; scanf("%d",&x);
printf("%lld\n",f(x));
}
return 0;
}
· Code 2
#include<cmath>
#include<cstdio>
typedef long long ll;
int Phi(int x){
int ans=x;
for(int i=2,lim=sqrt(x)+1;i<lim;i++) if(!(x%i)){
ans-=ans/i;
while(!(x%i)) x/=i;
}
return x>1?ans-ans/x:ans;
}
ll pow(ll a,ll n,ll p){
ll ans=1;
while(n){
if(n&1) ans=ans*a%p;
a=a*a%p; n>>=1;
}
return ans;
}
ll f(int x){
if(x==1) return 0;
int phi=Phi(x);
return pow(2,f(phi)+phi,x);
}
int main(){
int kase; scanf("%d",&kase);
while(kase--){
int x; scanf("%d",&x);
printf("%lld\n",f(x));
}
return 0;
}