题目大意:
给你一个数列。有两个操作:
1. 给一个区间内所有\(v_i\)的倍数除以\(v_i\)。
2. 询问区间和。
解题思路:
首先,建\(10^6\)棵平衡树(不要慌o((⊙﹏⊙))o)。
然后对操作离线。
对于每个初始的数\(a_i\),暴算出他的所有约数,并把\(i\)丢进与约数相应的平衡树里(如果修改中没出现这个约数则忽略即可)。
由于\(10^6\)内约数最多的数也只有200来个约数,所以不用担心空间问题。
接下来处理操作。
对于每个修改,我们直接在相应的平衡树中找到修改区间内的数,然后若\(a_i\)是v的倍数,则\(a_i\)除以v,若除完后\(a_i\)不是\(v\)的倍数了(或本身已经不是了),则在这个平衡树中删除这个数。
注意本身可能已经在其他v的修改中除掉了某个约数,所以仍然要判是否整除。
区间求和什么的,树状数组即可。
C++ Code:
#pragma GCC optimize("Ofast")
#pragma GCC optimize("unroll-loops")
#pragma GCC target("sse,sse2,sse3,ssse3,sse4,popcnt,abm,mmx,avx,tune=native")
#include<bits/stdc++.h>
#define reg register
using LoveLive=long long;
const int M=1e6+5,N=1e5+5;
LoveLive btr[M];
int n,m,a[N],opt[N],L[N],R[N],v[N],cnt=0,rt[M],nodes=0;
std::vector<int>V[1000005];std::stack<int>st;
struct node{
int ls,rs,r,v,sz;
}d[M<<6];
bool vis[M];
inline void add(int x,int d){for(;x<=M;x+=x&-x)btr[x]+=d;}
inline LoveLive ask(int x){reg LoveLive r=0;for(;x;x-=x&-x)r+=btr[x];return r;}
inline int readint(){
reg int c=getchar(),d=0;
for(;!isdigit(c);c=getchar());
for(;isdigit(c);c=getchar())
d=(d<<3)+(d<<1)+(c^'0');
return d;
}
inline int merge(int x,int y){
if(!x||!y)return x|y;
if(d[x].r<d[y].r){
d[x].rs=merge(d[x].rs,y);
return x;
}
d[y].ls=merge(x,d[y].ls);
return y;
}
inline void split(int u,int k,int&x,int&y){
if(!u)return void(x=y=0);
if(d[u].v<=k)
split(d[x=u].rs,k,d[u].rs,y);else
split(d[y=u].ls,k,x,d[u].ls);
}
inline void Delete(int&rt,int x){
reg int a,b;
split(rt,x-1,rt,a);
split(a,x,a,b);
rt=merge(rt,b);
}
int build(int l,int r,int&i){
if(l>r)return 0;
reg int nw=++nodes,mid=l+r>>1;
d[nw]=(node){0,0,rand(),V[i][mid]};
d[nw].ls=build(l,mid-1,i);
d[nw].rs=build(mid+1,r,i);
return nw;
}
void dfs(int now,int&v){
if(!now)return;
dfs(d[now].ls,v),dfs(d[now].rs,v);
int&nw=a[d[now].v];
if(nw%v==0){
add(d[now].v,nw/v-nw);
nw/=v;
}
if(nw%v)st.push(d[now].v);
}
int main(){
srand(time(0));
srand(rand());
n=readint(),m=readint();
for(reg int i=1;i<=n;++i)add(i,a[i]=readint());
for(reg int i=1;i<=m;++i){
opt[i]=readint(),L[i]=readint(),R[i]=readint();
if(opt[i]==1)vis[v[i]=readint()]=1;
}
//begin 寻找每个数的约数并将每个数的位置插入对应的平衡树
for(reg int i=1;i<=n;++i){
reg int&nw=a[i];
for(reg int j=1;j*j<=a[i];++j)
if(nw%j==0){
if(vis[j]&&j>1)V[j].push_back(i);
if(j*j<nw&&vis[nw/j])V[nw/j].push_back(i);
}
}
for(reg int i=2;i<M;++i)
if(V[i].size())rt[i]=build(0,V[i].size()-1,i);
//end
//begin 查询及修改
for(reg int i=1;i<=m;++i)
if(opt[i]==1){
if(v[i]>1){
reg int&nw=rt[v[i]],x,y;
split(nw,L[i]-1,nw,x);
split(x,R[i],x,y);
dfs(x,v[i]);
nw=merge(merge(nw,x),y);
while(!st.empty()){
reg int p=st.top();
st.pop();
split(nw,p-1,nw,x);
split(x,p,x,y);
nw=merge(nw,y);
}
}
}else printf("%lld\n",ask(R[i])-ask(L[i]-1));
//end
return 0;
}