前置知识:欧拉定理(扩展费马小定理),基本线段树操作
题意
十分简洁,不多赘述。
注意 (mod) 可以不为质数,每个单点除操作保证能整除。
思路
若 (mod) 是质数就好做了(不然怎么会是黑题),但 (mod) 不是质数代表在单点除时不能直接用费马小定理边乘边模。
可以考虑使用扩展费马定理:
[a^{varphi(p)}=1mod p ~~(gcd(a,p)=1)
]
发现小于 (1e9) 的每个数质因数个数不会超过 (9) 个,可以想到将所有要进行操作的数分成两部分,一部分质因子全是 (mod) 的质因子,开一个大小为 (10) 的数组暴力操作,若除就在这个数组里面减去要减的质因子,剩下部分的肯定与 (mod) 互质,除一个数就乘 (x^{varphi(mod)-1}) 并取模就好了。
注意细节,可以参考代码。
另附上 (varphi(n)) 的求法:
[varphi(n)=n imesprod^{x}_{i=1}frac{p_i}{p_i-1}
]
不要像我 (varphi) 求错了调1h
时间复杂度 ( exttt{O(10NlogN)}) (远远达不到这个上限)。
代码
#include <bits/stdc++.h>
using namespace std;
#define ls n << 1
#define rs n << 1 | 1
#define int long long
const int N = 1e5;
inline int read()
{
int s = 0;
register bool neg = 0;
register char c = getchar();
for (; c < '0' || c > '9'; c = getchar())
neg |= (c == '-');
for (; c >= '0' && c <= '9'; s = s * 10 + (c ^ 48), c = getchar())
;
s = (neg ? -s : s);
return s;
}
int a,mod,b,p[10],top,q[N+10],t[N+10][10],add[10],pp;
struct segment {
int l,r,val,lazy1[10],lazy2,lazy3;
} s[N*4+10];
inline int get_phi(int n) {
int ans = n;
for (int i = 2; i*i <= n; i++) {
if (!(n%i)) {
ans = 1ll * ans * (i - 1) / i;
while (!(n%i))n /= i;
}
}
if (n > 1)ans = 1ll * ans * (n - 1) / n;
return ans;
}
inline void get(int n) {
int qq=sqrt(n);
for(int i=2;i<=qq;i++) {
if(n%i==0) {
p[++top]=i;
while(n%i==0) n/=i;
}
}
if(n!=1) p[++top]=n;
}
inline int qpow(int n,int m) {
int res=1;
for(;m;m>>=1) {
if(m&1) res=res*n%mod;
n=n*n%mod;
}
return res;
}
inline int calc(int n) {
int res=1;
for(int i=1;i<=top;i++) res=(res*qpow(p[i],t[n][i]))%mod;
return res;
}
inline void up(int n) {
s[n].val=(s[ls].val+s[rs].val)%mod;
}
inline void down(int n) {
s[ls].val=s[ls].val*s[n].lazy2%mod;
s[rs].val=s[rs].val*s[n].lazy2%mod;
s[ls].lazy2=s[ls].lazy2*s[n].lazy2%mod;
s[rs].lazy2=s[rs].lazy2*s[n].lazy2%mod;
s[ls].lazy3=s[ls].lazy3*s[n].lazy3%mod;
s[rs].lazy3=s[rs].lazy3*s[n].lazy3%mod;
s[n].lazy2=s[n].lazy3=1;
for(int i=1;i<=top;i++) {
s[ls].lazy1[i]+=s[n].lazy1[i];
s[rs].lazy1[i]+=s[n].lazy1[i];
s[n].lazy1[i]=0;
}
return;
}
inline void build(int n,int l,int r) {
s[n].l=l;
s[n].r=r;
s[n].lazy2=s[n].lazy3=1;
memset(s[n].lazy1,0,sizeof(s[n].lazy1));
if(l==r) {
s[n].val=q[l]*calc(l)%mod;
return;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
up(n);
}
inline void div(int n,int m,int k) {
if(s[n].l==s[n].r) {
q[m]=q[m]*s[n].lazy3%mod;
s[n].lazy3=1;
for(int i=1;i<=top;i++) {
t[m][i]+=s[n].lazy1[i];
s[n].lazy1[i]=0;
while(k%p[i]==0) t[m][i]--,k/=p[i];
}
q[m]=q[m]*qpow(k,pp-1)%mod;
s[n].val=q[m]*calc(m)%mod;
return;
}
down(n);
int mid=(s[n].l+s[n].r)>>1;
if(m<=mid) div(ls,m,k);
else div(rs,m,k);
up(n);
}
inline void times(int n,int l,int r,int k,int _k) {
if(s[n].l>=l&&s[n].r<=r) {
s[n].val=s[n].val*k%mod;
s[n].lazy2=s[n].lazy2*k%mod;
s[n].lazy3=s[n].lazy3*_k%mod;
for(int i=1;i<=top;i++) s[n].lazy1[i]+=add[i];
return;
}
down(n);
int mid=(s[n].l+s[n].r)>>1;
if(l<=mid) times(ls,l,r,k,_k);
if(mid<r) times(rs,l,r,k,_k);
up(n);
}
inline int query(int n,int l,int r) {
if(s[n].l>=l&&s[n].r<=r) return s[n].val;
down(n);
int mid=(s[n].l+s[n].r)>>1,res=0;
if(l<=mid) res=(res+query(ls,l,r))%mod;
if(mid<r) res=(res+query(rs,l,r))%mod;
return res;
}
signed main()
{
// freopen("in.txt","r",stdin);
a=read();
mod=read();
pp=get_phi(mod);
int opt,x,y,z;
get(mod);
for(int i=1;i<=a;i++) {
q[i]=read();
for(int j=1;j<=top;j++) {
while(q[i]%p[j]==0) {
q[i]/=p[j];
t[i][j]++;
}
}
}
build(1,1,a);
b=read();
for(int i=1;i<=b;i++) {
opt=read();
x=read();
y=read();
if(opt==1) {
z=read();
if(z==1) continue;
int _z=z;
for(int j=1;j<=top;j++) {
add[j]=0;
while(_z%p[j]==0) _z/=p[j],add[j]++;
}
times(1,x,y,z,_z);
}
else if(opt==2) div(1,x,y);
else printf("%lld
",query(1,x,y));
}
return 0;
}