版权属于XP,想要引用此题(包括题面)的朋友请联系博主
分析:
首先看30%
我们知道根据欧拉定理:
aϕ(p)≡1
a
ϕ
(
p
)
≡
1
(mod p) (
a,p
a
,
p
互质)
当p是奇数的时候(一定互质),我们就可以将2的指数部分直接mod
ϕ(p)
ϕ
(
p
)
222...=2(22...)modϕ(p)
2
2
2
.
.
.
=
2
(
2
2
.
.
.
)
m
o
d
ϕ
(
p
)
同理指数部分的指数也可以mod ϕ(ϕ(p)) ϕ ( ϕ ( p ) )
222...=2(22...)modϕ(p)=2[2(2...)modϕ(ϕ(p))]modϕ(p)
2
2
2
.
.
.
=
2
(
2
2
.
.
.
)
m
o
d
ϕ
(
p
)
=
2
[
2
(
2
.
.
.
)
m
o
d
ϕ
(
ϕ
(
p
)
)
]
m
o
d
ϕ
(
p
)
但是当p是偶数的时候,不能直接使用欧拉定理了,怎么破?
没关系,我们让模数
p=2kq
p
=
2
k
q
,其中
q
q
是奇数,原式即为:
这样就可以使用欧拉定理了
使用快速幂,时间复杂度 O(qnlongn) O ( q n l o n g n )
然而,由于 ϕ(ϕ(p))<p2 ϕ ( ϕ ( p ) ) < p 2 ,所以 ϕ(p) ϕ ( p ) 在 O(logn) O ( l o g n ) 次嵌套后就会变成1,等到 phi(p)=1 p h i ( p ) = 1 ,再往上就没有必要了(mod 1=0)
ϕ(ϕ(p))<p2 ϕ ( ϕ ( p ) ) < p 2
- 如果p为奇数,那么 ϕ(p) ϕ ( p ) 最坏情况等于 p−1 p − 1 (质数),那么 phi(p) p h i ( p ) 一定是一个偶数
偶数的 ϕ ϕ 至多是 12 1 2 (偶数不会与偶数互质),因此 ϕ(ϕ(p))<p2 ϕ ( ϕ ( p ) ) < p 2- 如果p为偶数,偶数的 ϕ ϕ 至多是 12 1 2 ,因此 ϕ(ϕ(p))<p2 ϕ ( ϕ ( p ) ) < p 2
可以看到上面做法的复杂度已经完全可以通过100%的数据
但是现在的限制还是在于底数和模数不互质的问题,没法用欧拉定理
那么我们就需要欧拉定理的扩展包了
实际上
gcd(a,b)=1
g
c
d
(
a
,
b
)
=
1
和
gcd(a,b)!=1,b>=ϕ(p)
g
c
d
(
a
,
b
)
!
=
1
,
b
>=
ϕ
(
p
)
的情况可以合成一个
tip
mmp,写多了线筛,结果暴力求phi竟然懵了。。。
一开始看题解还是有一点一知半解,看了代码才明白(总结如下)
- 嵌套指数的模数一定是嵌套的 ϕ(p) ϕ ( p )
- 扩展欧拉定理只影响取模方式,并不影响模数
计算当前指数的时候(快速幂实际上是计算a[l]的指数的),取模方式由和当前模数的关系决定
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
const int N=100010;
int n,m,cnt=0;
ll p,pp,a[N],mod[N],f[N];
ll MOD(ll a,ll b) {
if (a<b) return a;
else return a%b+b;
}
ll KSM(ll a,ll b,ll mod) {
ll t=1;
while (b) {
if (b&1) t=MOD(t*a,mod);
b>>=1;
a=MOD(a*a,mod);
}
return t;
}
ll phi(ll a) {
ll b=a;
for (int i=2;i*i<=a;i++)
if (a%i==0) {
b=b/i*(i-1);
while (a%i==0) a/=i;
}
if (a!=1) b=b/a*(a-1);
return b;
}
ll solve(int l,int r,int dep) {
if (l>r||dep>=cnt) return 1;
ll pp=solve(l+1,r,dep+1); //指数
ll ans=KSM(a[l],pp,f[dep]); //(a[l]^pp)%f[dep]
return ans;
}
int main()
{
scanf("%d%lld",&n,&p);
f[++cnt]=p; //phi(phi(p))最多嵌套层数
while (f[cnt]!=1) f[++cnt]=phi(f[cnt-1]);
for (int i=1;i<=n;i++) scanf("%lld",&a[i]);
scanf("%d",&m);
int l,r;
for (int i=1;i<=m;i++) {
scanf("%d%d",&l,&r);
printf("%lld\n",solve(l,r,1)%p);
}
return 0;
}