首先正如官方题解所述“瓶颈在于如何找出所有该被/的数而不在于如何维护”。
考虑离线来完成这一步。对于每个操作
(id=这是第几个询问,l,r,x)
,在
l
处添加set<int> e[A+5];
,并e[x].insert(id)
)然后在
r+1
处添加
x
的删除标记。这样就可以知道每个数受哪些询问的影响。
然后对于一个set<int>
的*begin()
取出来,以询问的先后顺序为关键字建立一个堆。然后模拟
ai
不断除到1的情况,如果当前堆顶的数对应的x仍然是
ai
的因子,那么就把堆顶的迭代器++后扔回堆中,否则弹掉堆顶,这样做预处理时n*(建立大小为d的堆的时间+d次pop+(logw等于19)次push)的复杂度。
如果直接用普通的堆,那么是
ndlogd
。实测常数不是太大的话,是能AC的,尽管出题人扬言有空了要卡掉这种算法。
注意到pop的次数远大于push的次数,考虑快速(
O(n)
)建立,快速(
单次O(1)
)pop,慢速(单次
O(logn)
)push的数据结构。
然而这是不存在的,因为基于比较的排序是
O(nlogn)
的,而值域又是500000。
如果不考虑快速建立,那么平衡树是不错的选择,对于每个节点维护指向前驱后继的指针即可。
其实平衡树在原序列有序的情况下也能
O(n)
建立,然而每次直接sort
会多log。
对于这一点,把所有元素离线下来(离线套离线,离线算法的子算法仍要离线,有趣),然后基数排序即可。
就这样,基数排序+平衡树+set
+vector
+树状数组总算达到了std的复杂度,然而已经不想写了。
据说原始平衡树版本的std有4到5K,可能不比我这种算法好多少?
可是分块版本的std又快又短。
我这种算法也只能是娱乐大众了。
下面是我原始的
ndlogd
的代码
#pr\
agma GCC optimize("O2")
#include<vector>
#include<set>
#include<cstdio>
#include<cctype>
#include<queue>
using namespace std;
typedef long long ll;
inline void read(int&x){
char c=getchar();
for(;!isdigit(c);c=getchar());
for(x=0;isdigit(c);c=getchar())x=x*10+c-48;
}
const int N=100005,A=500000;
vector<int> d[A+5],b[N];
vector<pair<int,int> > op[N];
set<int> e[A+5];
set<int>::iterator it;
int n,i,m,l[N],r[N],x[N],o[N],a[N],y,j,k,z;
ll s[N];
inline void add(int i,int x){for(;i<=n;i+=i&-i)s[i]+=x;}
inline ll query(int i){register ll ans=0;for(;i;i-=i&-i)ans+=s[i];return ans;}
struct node{
int id;
set<int>::iterator it;
bool operator<(const node&rhs)const{
return *it>*rhs.it;
}
}xx;
struct pq{
static const int N=450;
int l[N],r[N],rt,d[N],q[N],t,w,t2;
node v[N];
int merge(int x,int y){
if(!x || !y)return x|y;
if(v[x]<v[y])t2=x,x=y,y=t2;
r[x]=merge(r[x],y);
if(d[r[x]]>d[l[x]])t2=l[x],l[x]=r[x],r[x]=t2;
d[x]=d[r[x]]+1;
return x;
}
inline void ini(){t=1,w=0;}
inline void pb(const node&x){
v[++w]=x;l[w]=r[w]=0;d[w]=1;q[w]=w;
}
inline void ini2(){
for(;t<w;t+=2)q[++w]=merge(q[t],q[t+1]);rt=q[w];
}
const node& top(){return v[rt];}
inline void pop(){rt=merge(l[rt],r[rt]);}
inline bool empty(){return !rt;}
inline bool push(const node&x){
v[++w]=x;l[w]=r[w]=0;d[w]=1;rt=merge(rt,w);
}
}qq;
int main(){
//freopen("1.txt","r",stdin);freopen("2.txt","w",stdout);
read(n),read(m);
for(i=1;i<=n;++i)read(a[i]),add(i,a[i]);
for(i=2;i<=A;++i)
for(j=i;j<=A;j+=i)d[j].push_back(i);
for(i=1;i<=m;++i){
read(o[i]);read(l[i]);read(r[i]);
if(o[i]==1)read(x[i]),b[l[i]].push_back(i),b[r[i]+1].push_back(i);
}
for(i=1;i<=n;++i){
for(j=0;j<(int)b[i].size();++j){
y=b[i][j];
if(i==l[y])e[x[y]].insert(y);
else e[x[y]].erase(y);
}
qq.ini();
for(j=0;j<(int)d[a[i]].size();++j){
y=d[a[i]][j];
if(!e[y].empty())qq.pb((node){y,e[y].begin()});
}
qq.ini2();
for(y=a[i];!qq.empty() && y>1;){
xx=qq.top();qq.pop();
it=xx.it;j=*it;
if(y%x[j]==0){
y/=x[j];
op[j].push_back(make_pair(i,y));
if(++it!=e[xx.id].end())qq.push((node){xx.id,it});
}
}
}
for(i=1;i<=m;++i){
for(j=0;j<(int)op[i].size();++j){
y=op[i][j].first,z=op[i][j].second;
add(y,z-a[y]);a[y]=z;
}
if(o[i]==2)printf("%lld\n",query(r[i])-query(l[i]-1));
}
return 0;
}