题解[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;
}

完结撒花❀

posted @ 2021-03-01 22:14  xxbbkk  阅读(53)  评论(0编辑  收藏  举报