zoukankan      html  css  js  c++  java
  • 【洛谷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;
    }
    
  • 相关阅读:
    7行代码看EntityFramework是如何运行
    我用ASP.NET缓存之SQL数据缓存依赖(SqlCacheDependency)
    利用Microsoft.Office.Interop.Excel 将web页面转成PDF
    IT农民的开发人员工具清单(2013年)
    我在项目中运用 IOC(依赖注入)--实战篇
    我在项目中运用 IOC(依赖注入)--入门篇
    我用ASP.NET缓存之数据缓存
    我用ASP.NET缓存之OutputCache
    Resharper 使用帮助-自动生成文件头
    玩转变量、环境变量以及数学运算(shell)
  • 原文地址:https://www.cnblogs.com/stoorz/p/12579284.html
Copyright © 2011-2022 走看看