Old Driver Tree(ODT 老驱动树)

O D T ODT ,中文称为老驱动树,又名珂朵莉树(虽然我看到老驱动莫名想笑)

O D T ODT 真是一个 暴力 神奇的东西

但是只有在数据随机且有区间覆盖操作的时候才有用(因为只有区间覆盖才会容易产生一段相同的区间)

因为考虑到每次覆盖了一个区间之后整个区间的数都是一样的了

于是就将这个区间缩成一个点,用 s e t set 维护一下所有点

这时候我们就可以方便的对其操作

具体实现:


初始化

考虑到每个点都是一段相同的区间

所以要维护区间左右端点和区间的值

用结构体实现

#define IT set<node>::iterator
#define pi pair<ll,int>
#define mp(x,y) make_pair(x,y)
struct node{
	int l,r;
	mutable ll val;
	node(int L,int R=-1,ll v=0):l(L),r(R),val(v){}
	bool operator < (const  node &a)const {return l<a.l;}
};

其主要操作就 2 2 个:

扫描二维码关注公众号,回复: 5084087 查看本文章

看代码应该很好理解

S p l i t : Split: 将一段区间分成两段并返回后一段的开头,用作分离出一段特定区间来处理询问

inline IT split(int pos){
	IT it=st.lower_bound(node(pos));
	if(it!=st.end()&&it->l==pos)return it;
	it--;
	int l=it->l,r=it->r;
	ll val=it->val;
	st.erase(it);
	st.insert(node(l,pos-1,val));
	return st.insert(node(pos,r,val)).first;
}

A s s i g n Assign: 用于处理区间覆盖,将一段区间提出来缩成一个点

inline void assign(int l,int r,ll val){
	IT itr=split(r+1),itl=split(l);
	st.erase(itl,itr);
	st.insert(node(l,r,val));	
}

另外几个操作(以CodeForces-896C为例)

A d d Add区间加 :直接遍历 s e t set l l ~ r r 中的所有点

inline void add(int l,int r,int val){
	IT itr=split(r+1),itl=split(l);
	for(;itl!=itr;itl++)itl->val+=val;
}

K t h Kth 查询区间第 k k 小:将区间所有点的信息 ( l , r , v a l ) (l,r,val) 加到一个 v e c t o r vector

按大小和数量遍历到第 k k 个输出就是了

inline ll kth(int l,int r,int k){
	vector<pi>vec;
	IT itr=split(r+1),itl=split(l);
	for(;itl!=itr;itl++)
	vec.push_back(mp(itl->val,itl->r-itl->l+1));
	sort(vec.begin(),vec.end());
	for(vector<pi>::iterator it=vec.begin();it!=vec.end();it++){
		k-=it->second;
		if(k<=0)return it->first;
	}
	return -1;
}

S u m Sum :区间所有数的 k k 次方之和

inline ll ksm(ll a,ll b,ll mod){
	int res=1;a%=mod;
	for(;b;b>>=1,a=a*a%mod){
		if(b&1)res=res*a%mod;
	}
	return res;
}
inline ll sum(int l,int r,ll x,ll y){
    ll res=0;
    IT itr=split(r+1),itl=split(l);
    for(;itl!=itr;++itl)
    res+=(ksm(itl->val,x,y)*((itl->r-itl->l+1)%y))%y,res%=y;
    return res;
}

感性理解一下复杂度 网上找不到证明

设区间数为 m m = n m,初始m=n

由初中数学可以证明每次 A s s i g n Assign 的期望长度约为 1 3 \frac13

不明白的可以自己百度

所以每次 A s s i g n Assign 会使 m m 变成 2 3 m \frac23m 并有概率产生2个新的区间

开始的时候相当于每次减少 1 3 \frac 13

后面由于 m m 少了产生的新区间也会有贡献

然后大概就是 O ( n l o g n ) O(nlogn) 了…

(赶紧逃)

猜你喜欢

转载自blog.csdn.net/qq_42555009/article/details/86580287
old
ODT