题解[CF906D Power Tower]
题目描述
长度为\(n\)的数组\(w\) ,模数\(P\)
\(m\)次询问,每次询问求\((w_l^{w_{l+1}^{w_{l+2}^{...^{w_r}}}}) \% P\)
Sol
这是一道对欧拉定理进行简单应用的题,难度还好。
看到次方套次方的就想到欧拉定理。具体的在这里就不说了。
只要用一个性质:
\[a^b \equiv a^b (mod P) ,b< \phi(P) ①
\]
\[a^b \equiv a^{b \% \phi(P)+\phi(P)}(mod P) ,b>=\phi(P)②
\]
然后我们一层一层“剥”掉次方,递归求解。
我们来对着代码说
快速幂
inline ll ksm(ll a,ll b,ll M){
ll res=1;
while(b){
if(b&1){
res=res*a;
if(res>M) res=res%M+M;//满足②式
}
a=a*a;
if(a>M) a=a%M+M;//满足②式
b>>=1;
}
return res;
}
递归
inline ll dfs(int id,ll M,int limit){
if(id==limit+1||M==1) return 1;//到达右端点或者φ变为1
//任何数模1都为0
int now=dfs(id+1,phi[M],limit);//now是当前a[id]要乘的次数
return ksm(a[id],now,M);//M是当前的模数
}
Code
#include<bits/stdc++.h>
#define ll long long
#define N (100010)
using namespace std;
int n,m;
ll P,a[N];
map<ll,int>phi;
inline ll read(){
ll w=0;
char ch=getchar();
while(ch>'9'||ch<'0') ch=getchar();
while(ch>='0'&&ch<='9'){
w=(w<<3)+(w<<1)+(ch^48);
ch=getchar();
}
return w;
}
inline ll Phi(ll x){
ll res=x;
for(int i=2;i*i<=x;i++){
if(x%i==0){
res=res/i*(i-1);
while(x%i==0) x=x/i;
}
}
if(x>1) res=res/x*(x-1);
return res;
}
inline ll ksm(ll a,ll b,ll M){
ll res=1;
while(b){
if(b&1){
res=res*a;
if(res>M) res=res%M+M;
}
a=a*a;
if(a>M) a=a%M+M;
b>>=1;
}
return res;
}
inline ll dfs(int id,ll M,int limit){
if(id==limit+1||M==1) return 1;
int now=dfs(id+1,phi[M],limit);
return ksm(a[id],now,M);
}
int main(){
n=read(),P=read();
for(int i=1;i<=n;i++) a[i]=read();
int tmp=P;
while(tmp!=1) phi[tmp]=Phi(tmp),tmp=phi[tmp];
phi[tmp]=1;
//把可能要用的预处理出来
m=read();
for(int i=1;i<=m;i++){
int l=read(),r=read();
printf("%lld\n",dfs(l,P,r)%P);
}
return 0;
}