ODT(old driver tree)详解(带例题)

版权声明:随意转载哦......但还是请注明出处吧: https://blog.csdn.net/dreaming__ldx/article/details/86551830

ODT简介

ODT(old driver tree 老驱动树)又名珂朵莉树是由 c o d e f o r c e s codeforces 上一位叫做 O D T ODT 的用户提出的一种基于平衡树的暴力数据结构。
这个数据结构的玄妙之处在于它
并没有稳定的时间复杂度
,因此只有在数据随机/水的情况下才会有较好的表现。

实现前提&&实现原理

前提是必须要有区间覆盖操作数据较随机/水
实现原理:将元素相同的区间推平一起处理,即如果区间 [ l , r ] [l,r] 中的所有数 [ a l , a l + 1 . . . a r ] [a_l,a_{l+1}...a_r] 全部相同的话就将这个区间的信息用一个节点表示。
从上述描述可以看出这个数据结构是非常暴力玄学

初始化

然后对于每段区间我们可以用一个三元组 [ l , r , v ] [l,r,v] 来表示它的左/右端点和整个区间的值,然后对于这些区间的信息可以用一个 s e t set 来维护。
下面是 O D T ODT 节点的初始化代码:

struct Node{
	int l,r;
	mutable int v;
	Node(int l,int r=-1,int v=0):l(l),r(r),v(v){}
	friend inline bool operator<(const Node&a,const Node&b){
		return a.l<b.l;
	}
};
set<Node>S;
typedef set<Node>::iterator It;

然后我们要谈谈 O D T ODT 两个重要的操作 s p l i t split a s s i g n assign

split操作

O D T ODT s p l i t split 函数 s p l i t ( p o s ) split(pos) 表示把 p o s pos 所在的这个区间 [ l , r , v ] [l,r,v] 分成 [ l , p o s 1 , v ] [l,pos-1,v] [ p o s , r , v ] [pos,r,v] 并且返回后者的迭代器。
实现很简单直接 l o w e r b o u n d lower_bound p o s pos 所在的区间然后加几个小特判。
代码:

inline It split(int pos){
	It it=S.lower_bound(pos);
	if(it!=S.end()&&it->l==pos)return it;
	--it;
	if(pos>it->r)return S.end();
	int l=it->l,r=it->r,v=it->v;
	S.erase(it);
	S.insert(Node(l,pos-1,v));
	return S.insert(Node(pos,r,v)).first;
}

assign操作

O D T ODT 最核心的操作 a s s i g n assign a s s i g n ( l , r , v ) assign(l,r,v) 表示把区间 [ l , r ] [l,r] 全部赋值为 v v 并且会将这 r l + 1 r-l+1 个点合并成一个 O D T ODT 节点插入到 O D T ODT 中,由于数据随机的时候 O D T ODT 中节点会快速减少,因此 O D T ODT 复杂度此时十分接近 O ( n l o g n ) O(nlog_n)
具体实现可以通过上面提到的 s p l i t split 函数。
代码:

inline void assign(int l,int r,int v){
	It R=split(r+1),L=split(l);
	S.erase(L,R),S.insert(Node(l,r,v));
}

其它操作

至此,与 O D T ODT 相关的操作已经基本讲完了。
剩下的操作与 O D T ODT 本身关系并不大,相当于就是遍历每一个 O D T ODT 节点暴力进行修改和查询。

区间第k小

代码:

typedef set<Node>iterator It;
typedef pair<long long,int> pli;
vector<pli>q;
inline int rank(int l,int r,int kth){
    It R=split(r+1),L=split(l);
    q.clear();
    for(It it=L;it!=R;++it)q.push_back(pli(it->v,it->r-it->l+1));
    sort(q.begin(),q.end());
    for(ri i=0;i<q.size();++i){
        kth-=q[i].second;
        if(kth<=0)return q[i].first;
    }
    return -1;
}

区间加

代码:

inline void add(int l,int r,int v){
    It L=split(l),R=split(r+1);
    for(It it=L;it!=R;++it)(it->v)+=v;
}

区间所有数的k次方和

代码:

typedef long long ll;
inline ll query(int l,int r,int k,ll mod){
    ll ret=0;
    It L=split(l),R=split(r+1);
    for(It it=L;it!=R;++it)ret=(ret+(ll)(it->r-it->l+1)*ksm(it->v,k,mod)%mod)%mod;
    return ret;
}

几道水题

c o d e f o r c e s 896 C codeforces896C
c o d e f o r c e s 915 E codeforces915E
c o d e f o r c e s 343 D codeforces343D
b z o j 4592 bzoj4592
2787 洛谷2787

猜你喜欢

转载自blog.csdn.net/dreaming__ldx/article/details/86551830
ODT
old