Description
给定正整数m<=1e9与n个数,n<=1e5,q个询问,q<=1e5,每次询问某个区间[l,r]的power值:
即
alal+1al+2...ar
a
l
a
l
+
1
a
l
+
2...
a
r
%m
???
扩展欧拉定理就适合这种求幂取模的破玩意
第一条其实可以与下面两条合并,然后下面把gcd!=1去掉。
即
abmodp=
a
b
mod
p
=
1.若
b<phi(p)
b
<
p
h
i
(
p
)
,a^b
2.若
b>=phi(p)
b
>=
p
h
i
(
p
)
,
abmodphi(p)+phi(p)
a
b
mod
p
h
i
(
p
)
+
p
h
i
(
p
)
具体证明可以网上找找,这里就不写了不会
然后我们可以发现,每一次询问,他的模数都会不断取phi,那么取phi时,奇数会变偶数,偶数会折半,那么最终到1只会有2 log m次左右。也就是说对一个数不断取他的phi,次数只有2 * log m次。
当l=r或p=1时可以直接return.
然后来解决b的问题,我们发现要找当前的b,只需要找到顺序第一个1,这个后面的都不用统计。 然后前面至少是2,是指数的指数级增长的,非常快(常数时间)就能判断出是否超过p了。
这样总的复杂度就是n log^2 n,还要带一些小常数,时限也开了4.5s。
#include <cstdio>
#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
typedef double db;
const ll N=1e5+10;
ll n,m,w[N],nx[N],q;
ll nphi[110];
ll min(ll a,ll b) {return a>b?b:a;}
ll gcd(ll a,ll b) {return b==0?a:gcd(b,a%b);}
ll ksm(ll a,ll b) {
if (b==0) return 1; if (b==1) return a;
ll t=ksm(a,b>>1);
if ((db)t*t*ksm(a,b&1)>1e9) return 1e9+1;
return t*t*ksm(a,b&1);
}
ll dmksm(ll a,ll b,ll mo) {
if (b==0) return 1%mo;
if (b==1) return a%mo;
ll t=dmksm(a,b>>1,mo);
return t*t%mo*dmksm(a,b&1,mo)%mo;
}
ll phi(ll x) {
ll ret=1;
for (int i=2; i*i<=x; i++) if (x%i==0) {
int cs=0; while (x%i==0) x/=i,cs++;
ret=ret*ksm(i,cs-1) * (i-1);
}
if (x!=1) ret*=(x-1);
return ret;
}
ll get(ll l,ll r) {
r=max(l,min(r,nx[l]-1));
ll t=w[r];
for (int i=r-1; i>=l; i--) {
t=ksm(w[i],t); if (t>1e9) return 1e9+1;
}
return t;
}
ll f(ll l,ll r,ll mc) {
ll mo=nphi[mc];
if (mo==1 || l==r) return w[l]%mo;
ll p=get(l+1,r);
if (p>nphi[mc]) return dmksm(w[l],f(l+1,r,mc+1)+nphi[mc+1],nphi[mc]);
else return dmksm(w[l],p,nphi[mc]);
}
int main() {
freopen("power.in","r",stdin);
freopen("power.out","w",stdout);
cin>>n>>m;
for (int i=1; i<=n; i++) scanf("%lld",&w[i]);
nx[n+1]=n+1;
for (int i=n; i; i--) nx[i]=w[i]!=1?nx[i+1]:i;
ll tmp=m; for (int i=1; i<=100; i++,tmp=phi(tmp)) nphi[i]=tmp;
cin>>q;
for (int i=1;i<=q;i++) {
int l,r; scanf("%d %d",&l,&r);
printf("%lld\n",f(l,r,1));
}
}