Link
首先考虑没有修改的情况。显然直接暴力Ex-Euler定理就行了,单次复杂度为(O(log p))的。
现在有了修改,我们可以树状数组维护差分数组,然后(O(log n))地单次查询单点值。
#include<bits/stdc++.h>
#define LL long long
using namespace std;
namespace IO
{
char ibuf[(1<<21)+1],obuf[(1<<21)+1],st[15],*iS,*iT,*oS=obuf,*oT=obuf+(1<<21);
char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
void Flush(){fwrite(obuf,1,oS-obuf,stdout),oS=obuf;}
void Put(char x){*oS++=x;if(oS==oT)Flush();}
int read(){int x=0;char ch=Get();while(ch>57||ch<48)ch=Get();while(ch>=48&&ch<=57)x=x*10+(ch^48),ch=Get();return x;}
void write(int x){int top=0;if(x<0)Put('-'),x=-x;if(!x)Put('0');while(x)st[++top]=(x%10)+48,x/=10;while(top)Put(st[top--]);Put('
');}
}
using namespace IO;
void max(int &a,int b){a=a>b? a:b;}
#define N 500007
#define M 20000007
int n,phi[M],p[M>>3],opt[N],l[N],r[N],P[N];
bitset<M>vis;
LL c[N];
void add(int p,int v){while(p<=n)c[p]+=v,p+=p&-p;}
LL query(int p){LL v=0;while(p)v+=c[p],p-=p&-p;return v;}
void modify(int l,int r,int v){add(l,v),add(r+1,-v);}
struct node{LL num;int flg;node(LL Num=0,int Flg=0):num(Num),flg(Flg){}};
node power(LL a,int k,int p)
{
node ans=node(1,0);
if(a>=p) a%=p,ans.flg=1;
while(k)
{
if(k&1) ans.num=ans.num*a;
if(ans.num>=p) ans.num%=p,ans.flg=1;
a*=a,k>>=1;
if(a>=p) a%=p,ans.flg=1;
}
return ans;
}
node calc(int l,int r,int p)
{
LL a=query(l);
if(p==1) return node(0,1);
if(a==1) return node(1,0);
if(l==r) return a<p? node(a,0):node(a%p,1);
int Phi=phi[p];node ans=calc(l+1,r,Phi);
if(ans.flg) ans.num+=Phi;
return power(a,ans.num,p);
}
int main()
{
n=read();int Q=read(),i,j,tot=0,Max=0;
for(i=1;i<=n;++i) tot=read(),modify(i,i,tot);
for(i=1;i<=Q;++i)
{
opt[i]=read(),l[i]=read(),r[i]=read(),P[i]=read();
if(opt[i]==2) max(Max,P[i]);
}
for(tot=0,phi[1]=1,i=2;i<=Max;++i)
{
if(!vis[i]) p[++tot]=i,phi[i]=i-1;
for(j=1;i*p[j]<=Max&&j<=tot;++j)
{
vis[i*p[j]]=1;
if(!(i%p[j])){phi[i*p[j]]=phi[i]*p[j];break;}
phi[i*p[j]]=phi[i]*(p[j]-1);
}
}
for(i=1;i<=Q;++i)if(opt[i]==1) modify(l[i],r[i],P[i]); else write(calc(l[i],r[i],P[i]).num);
return Flush(),0;
}