题意:
题解:
看到了幂次…如果要用二项式展开来做会很麻烦。然后发现其实总共的数不会超过30个,那么我们开线段树记录每个区间值为x的数有多少个,然后更新的话,直接记录每个数转移成了什么数(f数组的意义)
那么
num[root<<1][f[root][i]]=num[root<<1][i]
这样子去做就行了。
计算幂次的话,由于p不一定是个质数,直接取模会出错,并且如果在线段树里面快速幂的话,时间复杂度会很大,因此我们在输入k之后,如果op==3,将所有数的k次直接与处理出来。
#include<bits/stdc++.h>
using namespace std;
#define G if(++ip==ie)if(fread(ip=buf,1,SZ,stdin))
#define ri register int
#define iv inline void
using namespace std;
const int SZ=1<<19;
char buf[SZ],*ie=buf+SZ,*ip=ie-1;
inline int in(){
G;while(*ip<'-')G;
ri x=*ip&15;G;
while(*ip>'-'){x*=10;x+=*ip&15;G;}
return x;
}
const int N=1e5+5;
int p;
int num[N*4][35],f[N*4][35];
int tmp[35];
void push_down(int root){
for(int i=0;i<p;i++)
tmp[i]=num[root<<1][i],num[root<<1][i]=0;
for(int i=0;i<p;i++)
num[root<<1][f[root][i]]+=tmp[i];
for(int i=0;i<p;i++)
tmp[i]=num[root<<1|1][i],num[root<<1|1][i]=0;
for(int i=0;i<p;i++){
num[root<<1|1][f[root][i]]+=tmp[i];
f[root<<1][i]=f[root][f[root<<1][i]];
f[root<<1|1][i]=f[root][f[root<<1|1][i]];
}
for(int i=0;i<p;i++)
f[root][i]=i;
}
int a[N];
void build(int l,int r,int root){
for(int i=0;i<p;i++)
f[root][i]=i;
if(l==r){
num[root][a[l]]=1;
return ;
}
int mid=l+r>>1;
build(l,mid,root<<1);
build(mid+1,r,root<<1|1);
for(int i=0;i<p;i++)
num[root][i]=num[root<<1][i]+num[root<<1|1][i];
}
int quik(int a,int b){int ans=1;for(;b;b>>=1,a=a*a%p)if(b&1)ans=ans*a%p;return ans;}
int mod[N];
int qpow[35];
void update(int l,int r,int root,int ql,int qr,int op,int v){
if(l>=ql&&r<=qr){
if(op==2){//mul
for(int i=0;i<p;i++)
tmp[i]=num[root][i],num[root][i]=0;
for(int i=0;i<p;i++)
num[root][mod[i*v]]+=tmp[i],f[root][i]=mod[f[root][i]*v];
}
else if(op==1){//add
for(int i=0;i<p;i++)
tmp[i]=num[root][i],num[root][i]=0;
for(int i=0;i<p;i++)
num[root][mod[(i+v)]]+=tmp[i],f[root][i]=mod[(f[root][i]+v)];
}
else if(op==3){//^k
for(int i=0;i<p;i++)
tmp[i]=num[root][i],num[root][i]=0;
for(int i=0;i<p;i++)
num[root][qpow[i]]+=tmp[i],f[root][i]=qpow[f[root][i]];
}
return ;
}
push_down(root);
int mid=l+r>>1;
if(mid>=ql)
update(l,mid,root<<1,ql,qr,op,v);
if(mid<qr)
update(mid+1,r,root<<1|1,ql,qr,op,v);
for(int i=0;i<p;i++)
num[root][i]=num[root<<1][i]+num[root<<1|1][i];
}
int ans[35];
void query(int l,int r,int root,int ql,int qr){
if(l>=ql&&r<=qr){
for(int i=0;i<30;i++)ans[i]+=num[root][i];
return ;
}
push_down(root);
int mid=l+r>>1;
if(mid>=ql)
query(l,mid,root<<1,ql,qr);
if(mid<qr)
query(mid+1,r,root<<1|1,ql,qr);
}
int main()
{
int n;
n=in(),p=in();
for(int i=1;i<=n;i++)
a[i]=in(),a[i]%=p;
for(int i=0;i<35*35;i++)
mod[i]=i%p;
build(1,n,1);
int q;
q=in();
while(q--){
int op,l,r,k;
op=in(),l=in(),r=in(),k=in();
if(op<=2)k%=p;
else if(op==3){
for(int i=0;i<p;i++)
qpow[i]=quik(i,k);
}
if(op<=3)
update(1,n,1,l,r,op,k);
else {
for(int i=0;i<p;i++)ans[i]=0;
query(1,n,1,l,r);
if(op==4){
int sum=0;
for(int i=0;i<p;i++)
sum=(sum+quik(i,k)*ans[i])%p;
printf("%d\n",sum);
}
else {
int sum=1;
for(int i=0;i<p;i++)
sum=sum*quik(i,ans[i])%p;
printf("%d\n",sum);
}
}
}
return 0;
}