ODT 学习笔记
新年计划之学习 ODT \(\sim\) 完成!

超大图(
ODT 又称珂朵莉树,起源于 Codeforces Round #449 (Div. 1) 的 C 题 Willem, Chtholly and Seniorious。
ODT 是什么?
暴力数据结构。
例题
例题:CF896C,即上面那道题。
- 区间加
- 区间赋值
- 区间第 \(k\) 小
- 求区间幂次和
初始化
struct node{
int l, r;
mutable int x;
bool operator <(const node& T) const
{return l<T.l;}
node(int L, int R=-1, int X=0):l(L),r(R),x(X){}
};
表示 \(\forall i\in[l,r]\and i\in \mathbb{N^+},a_i=x\)。
用 mutable 是因为要在 set 中修改。
核心操作 split
将 \([l,r]\) 分裂成 \([l,p)\) 和 \([p,r]\)。(有一部分需要修改,另一部分不需要)
auto split(int pos){
auto it=s.lower_bound(node(pos));
if(it!=s.end()&&it->l==pos) return it;
--it;
int itl=it->l, itr=it->r;
ll itx=it->x;
s.erase(it);
s.insert(node(itl,pos-1,itx));
return s.insert(node(pos,itr,itx)).first;
}
推平操作 assign
即区间赋值。
将 \([l,r]\) 之间的数删除,并赋上一个数。(将多个点变成一个点,大大减少时间复杂度)
事实证明 assign 的操作次数可占到总操作次数的 \(\dfrac14\),将节点数优化到 \(\log\) 级别。
auto assign(int l, int r, ll x){
auto itr=split(r+1), itl=split(l);
s.erase(itl,itr);
s.insert(node(l,r,x));
}
记得一定要先 split(r+1) 再 split(l),因为如果先 split(l),返回的迭代器会位于所对应的区间以 \(l\) 为左端点,此时如果 \(r\) 也在这个节点内,就会导致 split(l) 返回的迭代器被 erase 掉,导致 \(\tt\color{purple}{RE}\)。
其他操作
暴力即可。
void add(int l, int r, ll delta){
auto itr=split(r+1), itl=split(l);
for(auto it=itl;it!=itr;++it)
it->x+=delta;
}
ll kth(int l, int r, int k){
auto itr=split(r+1), itl=split(l);
vector<pair<ll,int>> vec;
for(auto it=itl;it!=itr;++it)
vec.push_back(make_pair(it->x,it->r-it->l+1));
sort(vec.begin(),vec.end());
for(auto it:vec){
k-=it.second;
if(k<=0) return it.first;
}
}
ll qpow(ll a, int b, ll mod){
ll res=1ll;
a%=mod;
while(b){
if(b&1) res=res*a%mod;
a=a*a%mod;
b>>=1;
}
return res;
}
ll query(int l, int r, int ind, ll mod){
auto itr=split(r+1), itl=split(l);
ll res=0ll;
for(auto it=itl;it!=itr;++it)
res=(res+(it->r-it->l+1)*qpow(it->x,ind,mod)%mod)%mod;
return res;
}
ODT 固然好用,但容易被卡 /kk
适合当暴力骗分 /cy

浙公网安备 33010602011771号