【洛谷P3987】我永远喜欢珂朵莉~
题目
题目链接:https://www.luogu.com.cn/problem/P3987
给珂朵莉一个长为 \(n\) 的非负数序列 \(a\),支持以下两个操作:
1 l r x
: 把区间 \([l,r]\) 中所有 \(x\) 的倍数 \(\div x\)。2 l r
: 查询区间 \([l,r]\) 的和。
珂朵莉很可爱,所以你要帮珂朵莉写这个题。
\(1\leq n,m\leq 100000,0\leq ai\leq 500000,1 \leq x\leq 500000\)
思路
想找一道平衡树的练习码力(大概?)的题写写,然后就翻到了这题。
首先,在 \([1,500000]\) 的范围内,一个数最多有 \(200\) 个因子。
所以我们可以开 \(500000\) 棵平衡树,第 \(i\) 棵储存是 \(i\) 的倍数的数字的下标。这样平衡树最大只用开到 \(200n\)。
然后我们再开一个树状数组,用于求区间和。注意尽量不要用线段树,常数大。
对于一个修改操作1 l r x
,我们在第 \(x\) 棵平衡树中 \(dfs\),找到每一个位于 \([l,r]\) 的数字。然后在树状数组中找到这个数\(p\),将它变成 \(\frac{p}{x}\)。
如果此时 \(\frac{p}{x}\) 依然是 \(x\) 的倍数,那么将这个点保留在平衡树中不变,否则我们将这个点插入到栈中,最后将栈中的所有位置从第 \(x\) 棵平衡树中删除。
这样就简单的解决了这道题,显然每个数最多被除 \(\log\) 次(忽略除以 1 的情况),所以时间复杂度为 \(O(n\sqrt{n}\log n+nd\log n)\),不够优秀。
优化
- 吸氧。
- Treap是一棵笛卡尔树,所以我们可以在求出每一个数字的因数后,用一个 \(\operatorname{vector}\) 储存一个数 \(x\) 的倍数的下标。然后 \(O(n)\) 建树。时间复杂度\(O(n\sqrt{n}+nd\log n)\)。
- 我们其实不需要 500000 棵平衡树,只要将所有询问到的数字建平衡树即可。这样平衡树就从 500000 棵变成了 100000 棵。建树的复杂度也会减小。
- 换换随机种子(假)。
代码
#pragma GCC optimize("Ofast","inline")
#include <cctype>
#include <cstdio>
#include <vector>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define reg register
using namespace std;
typedef long long ll;
const int N=100010,M=500010,D=200,Inf=1e9;
int n,m,totel,a[N],rt[M],Q[N],opt[N],L[N],R[N],X[N],Y[N];
vector<int> qdel,fac[M];
inline int read()
{
int d=0; char ch=getchar();
while (!isdigit(ch)) ch=getchar();
while (isdigit(ch)) d=(d<<3)+(d<<1)+ch-48,ch=getchar();
return d;
}
inline void write(ll x)
{
if (x>9) write(x/10);
putchar(x%10+48);
}
struct Bit
{
ll c[N];
inline void add(int x,int v)
{
for (;x<=n;x+=x&-x)
c[x]+=v;
}
inline ll query(int x)
{
ll ans=0;
for (;x;x-=x&-x)
ans+=c[x];
return ans;
}
}bit;
struct Treap
{
int tot,lc[N*D],rc[N*D],val[N*D],dat[N*D];
inline int New(int v)
{
val[++tot]=v;
dat[tot]=rand();
return tot;
}
inline int build(int l,int r)
{
if (l>r) return 0;
int mid=(l+r)>>1,x=New(Q[mid]);
lc[x]=build(l,mid-1);
rc[x]=build(mid+1,r);
return x;
}
inline void zig(int &x)
{
int y=lc[x];
lc[x]=rc[y]; rc[y]=x; x=y;
}
inline void zag(int &x)
{
int y=rc[x];
rc[x]=lc[y]; lc[y]=x; x=y;
}
inline void ins(int &x,int v)
{
if (!x) x=New(v);
else if (v<val[x])
{
ins(lc[x],v);
if (dat[lc[x]]>dat[x]) zig(x);
}
else
{
ins(rc[x],v);
if (dat[rc[x]]>dat[x]) zag(x);
}
}
inline void del(int &x,int v)
{
if (!x) return;
if (val[x]==v)
{
if (lc[x] || rc[x])
{
if (!lc[x] || dat[rc[x]]>dat[lc[x]])
zag(x),del(lc[x],v);
else
zig(x),del(rc[x],v);
}
else x=0;
}
else if (v<val[x]) del(lc[x],v);
else del(rc[x],v);
}
}treap;
inline void insert(int i)
{
for (reg int j=2;j*j<=a[i];j++)
if (a[i]%j==0)
{
fac[j].push_back(i);
if (j*j!=a[i]) fac[a[i]/j].push_back(i);
}
if (a[i]>1) fac[a[i]].push_back(i);
}
inline void dfs(int x,int l,int r,int q)
{
if (!x) return;
int v=treap.val[x];
if (r>v) dfs(treap.rc[x],l,r,q);
if (l<v) dfs(treap.lc[x],l,r,q);
if (l<=v && r>=v)
{
int p=bit.query(v)-bit.query(v-1);
if (p%q) return;
bit.add(v,p/q-p);
if (p/q%q) qdel.push_back(v);
}
}
int main()
{
srand(540587);
n=read(); m=read();
for (reg int i=1;i<=n;i++)
{
a[i]=read();
insert(i); bit.add(i,a[i]);
}
for (reg int i=1;i<=m;i++)
{
opt[i]=read(); L[i]=read(); R[i]=read();
if (opt[i]==1) X[i]=Y[++totel]=read();
}
sort(Y+1,Y+1+totel);
totel=unique(Y+1,Y+1+totel)-Y-1;
for (reg int i=1;i<=totel;i++)
{
for (reg int j=0;j<fac[Y[i]].size();j++)
Q[j+1]=fac[Y[i]][j];
rt[Y[i]]=treap.build(1,fac[Y[i]].size());
}
for (reg int i=1;i<=m;i++)
{
if (opt[i]==1)
{
dfs(rt[X[i]],L[i],R[i],X[i]);
for (reg int j=0;j<qdel.size();j++)
treap.del(rt[X[i]],qdel[j]);
qdel.clear();
}
else write(bit.query(R[i])-bit.query(L[i]-1)),putchar(10);
}
return 0;
}