珂朵莉树(ODT)

  • Post author:
  • Post category:其他


珂朵莉树 ODT

主要内容

珂朵莉树是基于

数据随机且有整体赋值操作

而对序列操作的乱搞算法。

它的主要思想是用

set

维护若干个数值上相同的区间,并暴力处理其他询问。

建立



set

中,我们需要用结构体记录每个区间的信息:

struct NODE
{
	 int l,r;
	 mutable ll val; // mutable 表示可变的,便于在外边修改 val 的值
	 NODE(int L,int R=-1,ll Val=0):l(L),r(R),val(Vol){} // 相当于 (NODE){l,r,val}
	 bool friend operator < (NODE x,NODE y) { return x.l<y.l; } // 重载 <
};
set<NODE> s;

这样再

set

中就可以自动按照左端点排序了,注意在初始化的时候加入

\([n+1,n+1]\)

的区间。

split

珂朵莉树用

split

操作分裂出一堆区间后暴力操作。

inline auto split(int p)
{
	 auto it=s.lower_bound(NODE(p));
	 if(it!=s.end() && it->l==p) return it;
	 it--;
	 int l=it->l,r=it->r;
	 ll v=it->val;
	 s.erase(it);
	 s.insert(NODE(l,p-1,v));
	 return s.insert(NODE(p,r,v)).fi;
}

assign

如果只有

split

操作那

set

中元素岂不是爆炸?我们需要时不时将一些区间合并为一个区间才能保证复杂度。


\(\bigstar\texttt{attention}\)

:在推平的时候需要先

split

右端点再左端点,否则会 RE!

inline void assign(int l,int r,int v)
{
	 auto itr=s.split(r+1),itl=s.split(l);
	 s.erase(itl,itr); // 用将 [itl,itr) 之间的元素全部删除
	 s.insert(NODE(l,r,v));
}

其他操作

其他操作就可以直接暴力在

set

中搞了,下面以求区间内每个数的平方的和为例:

inline ll query(int l,int r)
{
	 auto itr=s.split(r+1),itl=s.split(l);
	 ll ret=0;
	 for(auto it=itl;it!=itr;it++)
	 	 ret+=1ll*(it->r-it->l+1)*it->v*it->v;
	 return ret;
}

复杂度

如果只有推平操作,即不需要将这个区间拎出来暴力处理,复杂度是

\(\mathcal{O(n\log n)}\)

如果有区间加等操作,就要基于数据随机了,这种情况下一定时间内进行一次推平可以达到

\(\mathcal{O(m\log n)}\)

的复杂度。

例题

起源题:

CF896C Willem, Chtholly and Seniorious


CF1638E Colorful Operations


在询问的时候把答案累加上去

,再套上一个树状数组利用前缀和维护区间加即可。


CF1423G Growing flowers

给你一个序列,支持以下两种操作:

  • 区间推平,即将

    \([l,r]\)

    中的数都改为同一个数

    \(x\)

  • 求序列中所有长度为

    \(k\)

    的子段中不同数的个数的和。


\(n,q\le 10^5\)

MatrixCascade:这题 800。

区间推平,所以考虑珂朵莉树。发现如果每次询问的时候再去统计,复杂度至少是和区间个数同阶,不可取。

考虑在推平的过程中更新答案。

将即将被推平的区间拎出来,将他们对答案减小的贡献减去。

对每一种数建立一个

set

,再用线段树记录方案数即可。



版权声明:本文为qq_53299575原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。