【洛谷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)\),不够优秀。

优化

  1. 吸氧。
  2. Treap是一棵笛卡尔树,所以我们可以在求出每一个数字的因数后,用一个 \(\operatorname{vector}\) 储存一个数 \(x\) 的倍数的下标。然后 \(O(n)\) 建树。时间复杂度\(O(n\sqrt{n}+nd\log n)\)
  3. 我们其实不需要 500000 棵平衡树,只要将所有询问到的数字建平衡树即可。这样平衡树就从 500000 棵变成了 100000 棵。建树的复杂度也会减小。
  4. 换换随机种子(假)。

代码

#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;
}
posted @ 2020-03-27 09:23  stoorz  阅读(...)  评论(...编辑  收藏