题目大意:
维护一个序列,支持将区间内的数x变为c^x,区间求和。
题解:
扩展欧拉定理
a^b=a^(b%phi(p)+phi(p)) ( mod p )
然后因为底数是确定的,所以一个数最多运行log(x)次就不会变了,因为phi(p)执行log(x)次后变为1
线段树暴力即可
代码:
#include<cstdio>
#include<algorithm>
using namespace std;
int n,mod,c,m,p,cnt,a[1000005],phi[1000005],tree[1000005],minn[1000005];
int get_phi(int x){
int ans=x;
for (int i=2; i*i<=x; i++)
if (x%i==0){
ans=ans/i*(i-1);
while (x%i==0) x/=i;
}
if (x!=1) ans=ans/x*(x-1);
return ans;
}
void build(int t,int l,int r){
if (l==r){
tree[t]=a[l];
return;
}
int mid=(l+r)>>1;
build(t<<1,l,mid);
build(t<<1|1,mid+1,r);
tree[t]=(tree[t<<1]+tree[t<<1|1])%mod;
}
int pow(int a,int b,int mod){
int ans=1;
while (b){
if (b&1) ans=1ll*ans*a%mod;
a=1ll*a*a%mod;
b=b>>1;
}
return ans;
}
int query(int a,int times){
for (int i=times; i>=1; i--){
if (a>=phi[i]) a=a%phi[i]+phi[i];
a=pow(c,a,phi[i-1]);
if (!a) a=phi[i-1];
}
return a;
}
void change(int t,int l,int r,int x,int y){
if (l>y || r<x) return;
if (minn[t]>=cnt) return;
if (l==r){
minn[t]++;
tree[t]=query(a[l],minn[t]);
return;
}
int mid=(l+r)>>1;
change(t<<1,l,mid,x,y);
change(t<<1|1,mid+1,r,x,y);
tree[t]=(tree[t<<1]+tree[t<<1|1])%mod;
minn[t]=min(minn[t<<1],minn[t<<1|1]);
}
int query(int t,int l,int r,int x,int y){
if (l>y || r<x) return 0;
if (l>=x && r<=y) return tree[t];
int mid=(l+r)>>1;
return (query(t<<1,l,mid,x,y)+query(t<<1|1,mid+1,r,x,y))%mod;
}
int main(){
scanf("%d%d%d%d",&n,&m,&p,&c);
for (int i=1; i<=n; i++)
scanf("%d",&a[i]);
mod=phi[0]=p;
while (p!=1){
phi[++cnt]=get_phi(p);
p=phi[cnt];
}
phi[++cnt]=1;
build(1,1,n);
while (m--){
int cas,l,r;
scanf("%d%d%d",&cas,&l,&r);
if (cas==0) change(1,1,n,l,r);
else printf("%d\n",query(1,1,n,l,r));
}
return 0;
}