分析
设最大值为 \(mx\),考虑每个数最多被除以 \(\log{mx}\) 次,那么加上树状数组的维护为 \(O(n\log{n}\log{mx})\)
问题就是如何快速找到这些位置,可以对于每一个约数单独把合法的数抽出来作为连续的一段用并查集维护。
那么一共需要抽出 \(O(mx\log{mx})\) 个位置,再加上并查集的维护也就是加上一个 \(\log\)。
总时间复杂度为 \(O(mx\log{mx}\log{n}+n\sqrt{mx}+n\log{n}\log{mx})\)。
代码
#include <cstdio>
#include <cctype>
#include <algorithm>
#define rr register
using namespace std;
const int N=500011; typedef long long lll; lll C[N],lans;
int l[N],r[N],c[N],b[N*40],f[N*40],n,m,mx,a[N];
inline lll iut(){
rr lll ans=0; rr char c=getchar();
while (!isdigit(c)) c=getchar();
while (isdigit(c)) ans=(ans<<3)+(ans<<1)+(c^48),c=getchar();
return ans;
}
inline void print(lll ans){
if (ans>9) print(ans/10);
putchar(ans%10+48);
}
inline void update(int x,int y){
for (;x<=n;x+=-x&x) C[x]+=y;
}
inline lll query(int l,int r){
rr lll ans=0; --l;
for (;r>l;r-=-r&r) ans+=C[r];
for (;l>r;l-=-l&l) ans-=C[l];
return ans;
}
inline signed getf(int u){return f[u]==u?u:f[u]=getf(f[u]);}
signed main(){
n=iut(); m=iut(),mx=1;
for (rr int i=1;i<=n;++i) a[i]=iut(),C[i]=C[i-1]+a[i];
for (rr int i=1;i<=n;++i)
if (a[i]>1) mx=mx>a[i]?mx:a[i],++c[a[i]];
for (rr int i=n;i;--i) C[i]-=C[i&(i-1)];
for (rr int i=2;i<=mx;++i)
for (rr int j=i+i;j<=mx;j+=i)
c[i]+=c[j];
for (rr int i=2,lst=0;i<=mx;++i) if (c[i]){
l[i]=lst+1,r[i]=l[i]+c[i]-1,lst=r[i],c[i]=0;
for (rr int j=l[i];j<=r[i];++j) f[j]=j;
}
for (rr int i=1;i<=n;++i)
if (a[i]>1){
b[l[a[i]]+c[a[i]]]=i,++c[a[i]];
for (rr int j=2;j*j<=a[i];++j)
if (a[i]%j==0){
b[l[j]+c[j]]=i,++c[j];
if (j*j<a[i]) b[l[a[i]/j]+c[a[i]/j]]=i,++c[a[i]/j];
}
}
for (rr int i=1;i<=m;++i){
rr int opt=iut();
rr int L=iut()^lans,R=iut()^lans;
if (opt==2) print(lans=query(L,R)),putchar(10);
else{
rr int x=iut()^lans;
if (x<2||x>mx||!c[x]) continue;
L=lower_bound(b+l[x],b+1+r[x],L)-b;
R=upper_bound(b+l[x],b+1+r[x],R)-b-1;
if (L>r[x]||L>R) continue;
for (rr int now=getf(L);now<=R;now=getf(now+1)){
if (a[b[now]]%x==0) update(b[now],a[b[now]]/x-a[b[now]]),a[b[now]]/=x;
if (now==R) break;
if (a[b[now]]%x) f[now]=getf(now+1);
}
}
}
return 0;
}