在太阳西斜的这个世界里,置身天上之森。
等这场战争结束后,不归之人和望眼欲穿的人们,人人本着正义之名。
长存不灭的过去,逐渐消逝的未来。
我回来了,纵使日薄西山,即便看不到未来,
此时此刻的光辉,盼君勿忘。
——世上最幸福的女孩
我永远喜欢珂朵莉~
5.20来切信仰题!
考虑操作 1 1 1,每次把一个区间内 x x x的倍数除以 x x x
- 当 x = 1 x=1 x=1的时候,显然什么都不用做
- 当 x ≥ 2 x\geq 2 x≥2的时候,一个数最坏情况下只会被除 log \log log次(每次都除 2 2 2)就会变成 1 1 1,然后就不能再除了
所以一共操作次数最多只有 n log r n\log r nlogr 次,其中 r = max { a i } r=\max\{a_i\} r=max{ai},即值域
那么接下来我们就需要考虑如何快速的找出区间内是 x x x的倍数的数
我们发现,对于任意 a i ≤ 5 × 1 0 5 a_i\leq 5\times 10^5 ai≤5×105,他最多最多只能有 a i \sqrt{a_i} ai个因数,所以我们考虑给每一个因数建立一棵平衡树储存所有的满足 a i a_i ai是这个因数的倍数的 i i i,那么查询的时候直接 FHQ Treap \text{FHQ Treap} FHQ Treap按照区间 s p l i t split split就可以
这些平衡树的建立我们可以提前利用 O ( n r ) O(n\sqrt{r}) O(nr)的时间进行预处理
那么接下来我们需要一种数据结构能够支持 单点修改、区间查询 那么这个时候我们就可以想到建立树状数组来维护这个东西
最多删除 n log r n\log r nlogr次,每次需要花费 log n \log n logn的时间
那么均摊下来的复杂度就是 O ( n r + n log n log r ) O(n\sqrt r+n\log n\log r) O(nr+nlognlogr),空间复杂度是 O ( n r ) O(n\sqrt r) O(nr)
在 n = 1 0 5 , r = 5 × 1 0 5 n=10^5,r=5\times 10^5 n=105,r=5×105,时限 4 s 4s 4s,空间限制 1.22 G B 1.22GB 1.22GB(这是个什么数???)的情况下看上去可以通过(Ynoi,你懂的
同时为了追求效率,我们应该每次建树的时候建出一棵笛卡尔树,否则会有几个点T掉,每次就把需要新建的部分存到数组里面然后暴力建树就可以(下面的 b u i l d build build部分),这样就保证了建树是严格的 O ( n ) O(n) O(n)而不会有多出来的 log \log log
做法很简单,但是坑还是不少,这里简单列几个我掉进去的坑吧
- 我们在预处理的时候为了方便可以拿一个 v e c t o r vector vector存相应的下标然后再扔到平衡树里,但是这个时候我们必须保证 v e c t o r vector vector单调递增
- 在遍历区间的时候,我们即使是除也需要先判断能不能除尽( 12 12 12分很可能就是挂在这里了),因为比如说有个 4 4 4,他先除了个 2 2 2,接下来又要除 4 4 4,那么这个时候他不是 4 4 4的倍数了,但是他还在 4 4 4那棵平衡树里面
卡常的话,其实这题也不怎么卡啦…
如果最后两个点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;
}