题意:链接
维护一个长度为n的数组,m个操作,支持两种操作:
0 l r表示将第l个到第r个数中的每一个数ai替换为c^ai;
1 l r求第l个到第r个数的和。
思路:
首先要知道扩展欧拉定理:如果
a
>
p
h
i
(
p
)
,
c
a
≡
c
(
a
m
o
d
  
p
h
i
(
p
)
)
+
p
h
i
(
p
)
m
o
d
  
p
a>phi(p) ,c^{a}\equiv c^{(a\mod phi(p))+phi(p)} \mod p
a>phi(p),ca≡c(amodphi(p))+phi(p)modp
在多次执行替换操作后,
a
[
i
]
a[i]
a[i]将会变为
c
c
c
.
.
.
m
o
d
  
p
h
i
(
1
)
+
p
h
i
(
1
)
c^{c^{c^{...}\mod phi(1) + phi(1) }}
ccc...modphi(1)+phi(1),然后它的值就不会在改变了。
假设在不断取
p
h
i
phi
phi的情况下,
p
p
p经过
k
k
k次变为1,易知
k
<
=
l
o
g
(
p
)
k<=log(p)
k<=log(p)
所以,我们可以用线段树维护区间和,以及区间最少的替换操作次数。
当它的替换操作次数小于
k
k
k时,直接暴力修改它的值。
当区间最少的替换操作次数都大于等于
k
k
k时,这段区间和就不会改变了。
时间复杂度
O
(
m
l
o
g
(
n
)
l
o
g
2
(
p
)
)
O(mlog(n)log^2(p))
O(mlog(n)log2(p))。
注意
p
h
i
phi
phi必须取到1,不能再
p
h
i
(
2
)
=
1
phi(2)=1
phi(2)=1时结束。
详见代码:
#include<cstdio>
#include<algorithm>
using namespace std;
#define MAXN 50010
int PowMod(int a,int b,int MO,bool &f)
{
int ret=1,t=0;
while(b)
{
if(b&1) f|=t|(1LL*ret*a>=MO),ret=1LL*ret*a%MO;
t|=(1LL*a*a>=MO);
a=1LL*a*a%MO;
b>>=1;
}
return ret;
}
int a[MAXN],p[MAXN],c,n,m,K;
int Work(int x,int dep)
{
int ret=x;
if(ret>=p[dep]) ret=ret%p[dep]+p[dep];
while(dep)
{
dep--;
bool flag=0;
ret=PowMod(c,ret,p[dep],flag);
if(flag) ret+=p[dep];
}
return ret%p[dep];
}
int Phi(int x)
{
int ret=x;
for(int i=2;1LL*i*i<=x;i++)
if(x%i==0)
{
while(x%i==0) x/=i;
ret=ret/i*(i-1);
}
if(x!=1) ret=ret/x*(x-1);
return ret;
}
int sum[MAXN*4],tag[MAXN*4];
void PushUp(int i)
{
sum[i]=(sum[i<<1]+sum[i<<1|1])%p[0];
tag[i]=min(tag[i<<1],tag[i<<1|1]);
}
void Build(int i,int l,int r)
{
if(l==r)
{
sum[i]=a[l];tag[i]=0;
return;
}
int mid=(l+r)>>1;
Build(i<<1,l,mid);
Build(i<<1|1,mid+1,r);
PushUp(i);
}
void Modify(int i,int l,int r,int L,int R)
{
if(tag[i]==K) return;
if(l==r)
{
tag[i]++;
sum[i]=Work(a[l],tag[i]);
return;
}
int mid=(l+r)>>1;
if(L<=mid) Modify(i<<1,l,mid,L,R);
if(R>mid) Modify(i<<1|1,mid+1,r,L,R);
PushUp(i);
}
int Ask(int i,int l,int r,int L,int R)
{
if(L<=l&&r<=R) return sum[i];
int ret=0,mid=(l+r)>>1;
if(L<=mid) ret=Ask(i<<1,l,mid,L,R);
if(R>mid) ret=(ret+Ask(i<<1|1,mid+1,r,L,R))%p[0];
return ret;
}
int main()
{
scanf("%d%d%d%d",&n,&m,&p[0],&c);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
while(p[K]!=1) p[++K]=Phi(p[K-1]); p[++K]=1;
Build(1,1,n);
int f,l,r;
while(m--)
{
scanf("%d%d%d",&f,&l,&r);
if(f==0) Modify(1,1,n,l,r);
else printf("%d\n",Ask(1,1,n,l,r));
}
}