虽然挺好想,但是很难写。
还是第一次写带标记永久化的区间操作,区间查询线段树。
修改时一直根据实际长度改值,被包含时改标记。
查询时被包含算值,否则一直根据实际长度算标记。
#include <bits/stdc++.h>
using namespace std;
const int M=1e9+7;
const int C=62;
typedef long long lll;
const int N=400010;
int a[N];
struct node{
int val,tag;
lll app,pag;
}T[1<<20];
int p[C],inv[C];
lll rep(int x){
lll ret=0;
for (int i=0; i<C; ++i) if (x%p[i]==0) ret|=1ll<<i;
return ret;
}
bool b[400];
int fp(int x,int y){
int ret=1;
for (; y; y>>=1,x=(lll)x*x%M) if (y&1) ret=(lll)ret*x%M;
return ret;
}
int ll,rr,cc,n,q;
lll cf;
void build(int ind,int l,int r){
T[ind].tag=1;
if (l==r){
T[ind].val=a[l];
T[ind].app=rep(a[l]);
return;
}
int mid=(l+r)>>1;
build(ind<<1,l,mid);
build(ind<<1|1,mid+1,r);
T[ind].app=T[ind<<1].app|T[ind<<1|1].app;
T[ind].val=(lll)T[ind<<1].val*T[ind<<1|1].val%M;
}
void update(int ind,int l,int r){
T[ind].app|=cf;
lll d=fp(cc,min(r,rr)-max(l,ll)+1);
T[ind].val=T[ind].val*d%M;
if (ll<=l&&r<=rr){
//cerr<<"CD"<<cc<<" "<<l<<" "<<r<<endl;
T[ind].tag=(lll)T[ind].tag*cc%M;
T[ind].pag|=cf;
return;
}
int mid=(l+r)>>1;
if (ll<=mid) update(ind<<1,l,mid);
if (mid<rr) update(ind<<1|1,mid+1,r);
}
void ask(int ind,int l,int r){
if (ll<=l&&r<=rr){
//cerr<<l<<" "<<r<<" "<<T[ind].val<<endl;
cc=(lll)T[ind].val*cc%M;
cf|=T[ind].app;
return;
}
cc=(lll)fp(T[ind].tag,(min(r,rr)-max(l,ll)+1))*cc%M;
cf|=T[ind].pag;
int mid=(l+r)>>1;
if (ll<=mid) ask(ind<<1,l,mid);
if (mid<rr) ask(ind<<1|1,mid+1,r);
}
int main(){
int pnum=0;
for (int i=2; i<=300; ++i){
if (b[i]) continue;
p[pnum++]=i;
for (int j=i*i; j<=300; j+=i)
b[j]=1;
}
for (int i=0; i<pnum; ++i) inv[i]=fp(p[i],M-2);
scanf("%d%d",&n,&q);
for (int i=1; i<=n; ++i){
scanf("%d",&a[i]);
}
build(1,1,n);
for (int i=1; i<=q; ++i){
char s[10];
scanf("%s%d%d",s,&ll,&rr);
if (s[0]=='T'){
cc=1; cf=0;
ask(1,1,n);
//cerr<<"CC"<<cc<<endl;
for (int i=0; i<C; ++i) if ((cf>>i)&1) cc=(lll)cc*inv[i]%M*(p[i]-1)%M;
cout<<cc<<'\n';
}
else{
scanf("%d",&cc);
cf=rep(cc);
update(1,1,n);
}
}
}
快速幂预处理分类讨论一下,应该也可一个\(log\)