在太阳西斜的这个世界里,置身天上之森。
等这场战争结束后,不归之人和望眼欲穿的人们,人人本着正义之名。
长存不灭的过去,逐渐消逝的未来。
我回来了,纵使日薄西山,即便看不到未来,
此时此刻的光辉,盼君勿忘。
——世上最幸福的女孩
我永远喜欢珂朵莉~
5.20来切信仰题!
考虑操作111,每次把一个区间内xxx的倍数除以xxx
- 当x=1x=1x=1的时候,显然什么都不用做
- 当x≥2x\geq 2x≥2的时候,一个数最坏情况下只会被除log\loglog次(每次都除222)就会变成111,然后就不能再除了
所以一共操作次数最多只有nlogrn\log rnlogr 次,其中r=max{ai}r=\max\{a_i\}r=max{ai},即值域
那么接下来我们就需要考虑如何快速的找出区间内是xxx的倍数的数
我们发现,对于任意ai≤5×105a_i\leq 5\times 10^5ai≤5×105,他最多最多只能有ai\sqrt{a_i}ai个因数,所以我们考虑给每一个因数建立一棵平衡树储存所有的满足aia_iai是这个因数的倍数的iii,那么查询的时候直接FHQ Treap\text{FHQ Treap}FHQ Treap按照区间splitsplitsplit就可以
这些平衡树的建立我们可以提前利用O(nr)O(n\sqrt{r})O(nr)的时间进行预处理
那么接下来我们需要一种数据结构能够支持 单点修改、区间查询 那么这个时候我们就可以想到建立树状数组来维护这个东西
最多删除nlogrn\log rnlogr次,每次需要花费logn\log nlogn的时间
那么均摊下来的复杂度就是O(nr+nlognlogr)O(n\sqrt r+n\log n\log r)O(nr+nlognlogr),空间复杂度是O(nr)O(n\sqrt r)O(nr)
在n=105,r=5×105n=10^5,r=5\times 10^5n=105,r=5×105,时限4s4s4s,空间限制1.22GB1.22GB1.22GB(这是个什么数???)的情况下看上去可以通过(Ynoi,你懂的
同时为了追求效率,我们应该每次建树的时候建出一棵笛卡尔树,否则会有几个点T掉,每次就把需要新建的部分存到数组里面然后暴力建树就可以(下面的buildbuildbuild部分),这样就保证了建树是严格的O(n)O(n)O(n)而不会有多出来的log\loglog
做法很简单,但是坑还是不少,这里简单列几个我掉进去的坑吧
- 我们在预处理的时候为了方便可以拿一个vectorvectorvector存相应的下标然后再扔到平衡树里,但是这个时候我们必须保证vectorvectorvector单调递增
- 在遍历区间的时候,我们即使是除也需要先判断能不能除尽(121212分很可能就是挂在这里了),因为比如说有个444,他先除了个222,接下来又要除444,那么这个时候他不是444的倍数了,但是他还在444那棵平衡树里面
卡常的话,其实这题也不怎么卡啦…
如果最后两个点T了可以看看是不是写了# define int long long,就把树状数组开longlong就够了,能快三分之一
写完这题顺便还可以把大学给A了
代码:
#include <bits/stdc++.h>
using namespace std;
# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)
typedef long long ll;
const int N=5e5+5;
const int M=34e6+5;
template<typename T> void read(T &x){
x=0;int f=1;
char c=getchar();
for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
x*=f;
}
int n,m,top;
int a[N];
int root[N],tot;
int q[N],len;
int son[M][2],siz[M],val[M];
ll bit[N];
int bin[M],binsiz;
vector<int> fac[N];
void add(int o,int x){
for(;o<=n;o+=o&-o)bit[o]+=x;
}
ll ask(int o){
ll res=0;
for(;o;o-=o&-o)res+=bit[o];
return res;
}
int newnode(int x){
int u=(!binsiz)?++tot:bin[binsiz--];
siz[u]=1;
val[u]=x;
return u;
}
void update(int x){
siz[x]=siz[son[x][0]]+siz[son[x][1]]+1;
}
int build(int l,int r){
if(l>r)return 0;
int mid=l+r>>1;
int u=newnode(q[mid]);
son[u][0]=build(l,mid-1);
son[u][1]=build(mid+1,r);
return update(u),u;
}
int merge(int u,int v){
if(!u||!v)return u|v;
int rt;
if(rand()<rand())son[rt=u][1]=merge(son[u][1],v);
else son[rt=v][0]=merge(u,son[v][0]);
return update(rt),rt;
}
void split(int o,int &u,int &v,int k){
if(!o){u=v=0;return;}
if(val[o]<=k)split(son[u=o][1],son[o][1],v,k);
else split(son[v=o][0],u,son[o][0],k);
update(o);
}
void del(int &rt,int x){
int lft,mid,rht;
split(rt,lft,rht,x);
split(lft,lft,mid,x-1);
rt=merge(lft,rht);
}
void dfs(int u,int k){
if(!u)return;
if(son[u][0])dfs(son[u][0],k);
if(a[val[u]]%k==0)add(val[u],-a[val[u]]+a[val[u]]/k),a[val[u]]/=k;
if(a[val[u]]%k==0)q[++len]=val[u];
if(son[u][1])dfs(son[u][1],k);
bin[++binsiz]=u;
son[u][0]=son[u][1]=siz[u]=val[u]=0;
}
int main()
{
srand(time(0));
read(n),read(m);
Rep(i,1,n)read(a[i]),top=max(top,a[i]),add(i,a[i]);
Rep(i,1,n)
for(int j=1;j*j<=a[i];j++)
if(a[i]%j==0){
fac[j].push_back(i);
if(a[i]/j!=j)fac[a[i]/j].push_back(i);
}
Rep(i,2,top){
len=0;
for(vector<int>::iterator it=fac[i].begin();it!=fac[i].end();it++)
q[++len]=*it;
root[i]=build(1,len);
}
Rep(i,1,m){
int opt,l,r,k;
read(opt),read(l),read(r);
if(opt==1){
read(k);
if(k==1)continue;
int lft,mid,rht;
split(root[k],lft,rht,r);
split(lft,lft,mid,l-1);
len=0;
dfs(mid,k);
mid=build(1,len);
root[k]=merge(merge(lft,mid),rht);
}
else printf("%lld\n",ask(r)-ask(l-1));
}
return 0;
}

360





