题目链接
欧拉降幂最多只会迭代到第 \(k = \text O (\log p)\) 次模数就会变成 \(1\)(证明见这里)。故一个数被操作超过 \(k\) 次就会变为一个定值。
于是用线段树维护出操作还没有满 \(k\) 次的位置,每次暴力修改并计算,均摊下来只有 \(nk\) 次。光速幂可以预处理。
时间复杂度 \(\text O (k \sqrt p + nk \log n + nk ^ 2)\)。
#include<cstdio>
#include<cmath>
#define N 50005
#define M 55
using namespace std;int n,m,p,c,k,a[N],mo[M];
int pw[M][N],Pw[M][N];
int mod(long long x,int p) {if(x<p) return x;return x%p+p;
}
int phi(int x) {int res=x;for(int i=2;i<=x/i;i++) if(x%i==0) {res=res/i*(i-1);while(x%i==0) x/=i;}if(x>1) res=res/x*(x-1);return res;
}
int qqpow(int x,int y,int p) {return mod(1ll*pw[p][y&(1<<14)-1]*Pw[p][y>>14],mo[p]);
}
int calc(int x,int p,int d) {if(p>d) return mod(x,mo[p]);return qqpow(x,calc(x,p+1,d),p);
}
struct segment_tree {struct st {int l,r,c,cnt,sum; bool tag;#define l(p) tr[p].l#define r(p) tr[p].r#define c(p) tr[p].c#define cnt(p) tr[p].cnt#define sum(p) tr[p].sum#define tag(p) tr[p].tag} tr[N*4];void update(int p) {sum(p)=(sum(p*2)+sum(p*2+1))%mo[1];tag(p)=tag(p*2)|tag(p*2+1);}void build(int p,int l,int r) {l(p)=l,r(p)=r,tag(p)=1;if(l==r) {c(p)=sum(p)=a[l]; return;}int mid=l+r>>1;build(p*2,l,mid),build(p*2+1,mid+1,r);update(p);}void modify(int p,int l,int r) {if(!tag(p)) return;if(l(p)==r(p)) {cnt(p)++,sum(p)=calc(c(p),1,cnt(p))%mo[1],tag(p)=cnt(p)<k;return;}if(l<=r(p*2)) modify(p*2,l,r);if(r>=l(p*2+1)) modify(p*2+1,l,r);update(p);}int ask(int p,int l,int r) {if(l<=l(p)&&r>=r(p)) return sum(p);if(r<=r(p*2)) return ask(p*2,l,r);if(l>=l(p*2+1)) return ask(p*2+1,l,r);return (ask(p*2,l,r)+ask(p*2+1,l,r))%mo[1];}
} t1;
int main() {scanf("%d%d%d%d",&n,&m,&p,&c);for(int i=1;i<=n;i++) scanf("%d",&a[i]);while(p>1) mo[++k]=p,p=phi(p);mo[++k]=p;for(int i=1;i<=k;i++) {int w=1;for(int j=0;j<1<<14;j++) pw[i][j]=w,w=mod(1ll*w*c,mo[i]);int cc=w; w=1;for(int j=0;j<1<<14;j++) Pw[i][j]=w,w=mod(1ll*w*cc,mo[i]);}mo[k+1]=1,t1.build(1,1,n);for(int i=1,op,x,y;i<=m;i++) {scanf("%d%d%d",&op,&x,&y);if(!op) t1.modify(1,x,y);else printf("%d\n",t1.ask(1,x,y));}return 0;
}