题目链接https://acm.ecnu.edu.cn/contest/173/problem/E/
题意:
在一个数组上有三种操作
- 区间乘最小素因子
- 区间除最小素因子
- 查询单点值
题解:
因为如果先乘后除就会抵消,所以最后一定是:除除除除乘乘乘乘,
维护一个线段树的lazy标记,记录每一个区间的除法个数和乘法个数。做除法的时候,先在乘法个数里面减,然后有剩余在除法个数里面加。做乘法就直接往乘法个数里加。
因为有pushdown操作的存在,所以保证了顺序是先做子区间标记的除和乘,再做父区间标记的除和乘。
还有就是做质因数分解的时候,要随时判断素数来优化。
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=1e6+7;
const ll mod=1e9+7;
ll n;
ll d[N<<2],t[N<<2];
ll a[N];
ll pri[N],tot;
bool vis[N];
void init(){
memset(vis,false,sizeof(vis));
for(ll i=2;i<N;i++){
if(!vis[i]) pri[++tot]=i;
for(ll j=1;j<=tot&&i*pri[j]<N;j++){
vis[i*pri[j]]=true;
if(i%pri[j]==0) break;
}
}
}
void upd(ll rt,ll p){
ll dd=d[p],tt=t[p];
if(t[rt]<=dd) dd-=t[rt],t[rt]=0;
else t[rt]-=dd,dd=0;
d[rt]+=dd;
t[rt]+=tt;
}
void pd(ll rt){
upd(rt<<1,rt);
upd(rt<<1|1,rt);
d[rt]=t[rt]=0;
}
void tim(ll rt,ll l,ll r,ll L,ll R){
if(L<=l&&r<=R){
t[rt]++;
return;
}
pd(rt);
ll m=l+r>>1;
if(L<=m) tim(rt<<1,l,m,L,R);
if(m<R) tim(rt<<1|1,m+1,r,L,R);
}
void div(ll rt,ll l,ll r,ll L,ll R){
if(L<=l&&r<=R){
if(t[rt]) t[rt]--;
else d[rt]++;
return;
}
pd(rt);
ll m=l+r>>1;
if(L<=m) div(rt<<1,l,m,L,R);
if(m<R) div(rt<<1|1,m+1,r,L,R);
}
ll que(ll rt,ll l,ll r,ll x){
if(l==r) return rt;
pd(rt);
ll m=l+r>>1;
if(x<=m) return que(rt<<1,l,m,x);
else return que(rt<<1|1,m+1,r,x);
}
ll qpow(ll a,ll b){
ll res=1;
while(b){
if(b&1) res=res*a%mod;
b>>=1;a=a*a%mod;
}
return res;
}
int main(){
init();
// printf("%lld\n",tot);
ll n,m;
scanf("%lld%lld",&n,&m);
for(ll i=1;i<=n;i++){
scanf("%lld",&a[i]);
}
ll o,x,y;
while(m--){
scanf("%lld",&o);
if(o==1){
scanf("%lld%lld",&x,&y);
tim(1,1,n,x,y);
}
else if(o==2){
scanf("%lld%lld",&x,&y);
div(1,1,n,x,y);
}
else{
scanf("%lld",&x);
ll r=que(1,1,n,x);
ll ans=a[x];
for(ll i=1,j=1; ans>1 && i<=tot && j<=d[r]; i++){
if(!vis[ans]){
ans=1;
break;
}
while((j<=d[r]) && (ans%pri[i]==0)){
ans/=pri[i];
j++;
}
}
if(!vis[ans]) ans=ans*qpow(ans,t[r])%mod;
else{
for(ll i=1;i<=tot;i++){
if((ans%pri[i])==0){
ans=ans*qpow(pri[i],t[r])%mod;
break;
}
}
}
printf("%lld\n",ans);
}
}
}