珂朵莉樹學習筆記
阿新 • • 發佈:2021-09-28
一個神奇的資料結構 , 當資料隨機情況下可以亂殺
又名珂朵莉樹
基本概念與條件性
利用set維護序列的資料結構 , 將原序列分為了若干塊 , 用三元組(l,r,sum)表示
例如:2 3 3 3 4 2 7 7 8
\((1,1,2) , (2,4,3) , (5,5,4) , (6,6,2) , (7,8,7) , (9,9,8)\)
顯然這個資料結構是一種基於暴力的資料結構,非常不穩定
若使用 \(ODT\) 做題,則該題目必須保證
\(1\).資料足夠弱
\(2\).有區間推平操作
這樣才能保證其複雜度
初始化函式
struct node{ int l,r; mutable int sum; // mutable可以讓set中的sum成為可變元素!!切記 friend bool operator <(node A,node B) { return A.l < B.l; } }; set<node> s;
珂朵莉樹有兩個核心函式
一個是\(spilt\)保證功能性
另一個是\(assign\)保證其複雜度
\(split(l,r)\)
為了避免像分塊那樣整塊和散塊分別遍歷 , 珂朵莉樹創造了這個分裂函式
可以把一個序列中\([l,r]\)這段區間給獨立出來
inline void split(int l,int r) { sto it = s.upper_bound({l,l,0}); // 查詢左端點所在塊 it--; if((*it).l != l) // 如果l並不是該塊的左端點 { node p = *it; s.erase(it); s.insert({p.l , l-1,p.sum}); s.insert({l , p.r , p.sum});//分列 } it =s.upper_bound({r,r,0});//對r同理 it--; if((*it).r != r) { node p = *it; s.erase(it); s.insert({r+1,p.r , p.sum}); s.insert({p.l , r, p.sum}); } }
\(assigne\)
推平操作,用以保持複雜度
先\(split(l,r)\)然後然後順序刪掉
\(erase\)函式支援區間刪除 , 不過是左閉右開的 , 並且你需要傳指標
inline void assign(int l,int r,int val)
{
split(l,r);
s.erase(s.lower_bound({l,l,0}) , s.upper_bound({r,r,0}));
s.insert({l,r,val});
}
珂朵莉的靈魂就在於這兩個核心函式 , 剩下的則需要考場上隨機應變,憑本事騙分了
\(Code\)
#include<bits/stdc++.h> using namespace std; #define INF 1ll<<30 #define int long long #define exp 1e-7 #define db double #define us unsigned #define pii pair<int,int> #define fi first #define sc second #define sto set<node>::iterator const int mod = 1e9+7; const int p = 3e5+5; template<typename _T> inline void read(_T &x) { x= 0 ;int f =1;char s=getchar(); while(s<'0' ||s>'9') {f =1;if(s == '-')f =- 1;s=getchar();} while('0'<=s&&s<='9'){x = (x<<3) + (x<<1) + s - '0';s= getchar();} x*=f; } struct node{ int l,r; mutable int sum; friend bool operator <(node A,node B) { return A.l < B.l; } }a[p],b[p]; set<node> s; inline void split(int l,int r) { sto it = s.upper_bound({l,l,0}); it--; if((*it).l != l) { node p = *it; s.erase(it); s.insert({p.l , l-1,p.sum}); s.insert({l , p.r , p.sum}); } it =s.upper_bound({r,r,0}); it--; if((*it).r != r) { node p = *it; s.erase(it); s.insert({r+1,p.r , p.sum}); s.insert({p.l , r, p.sum}); } } inline void assign(int l,int r,int val) { split(l,r); s.erase(s.lower_bound({l,l,0}) , s.upper_bound({r,r,0})); s.insert({l,r,val}); } inline int query(int l,int r) { split(l,r); sto it = s.lower_bound({l,l,0}); int ans = 0; for(;it!=s.end() && (*it).r<=r;++it) (ans += (*it).sum * ((*it).r - (*it).l +1))%=mod; return ans; } void add(int l,int r,int val) { split(l,r); sto it = s.lower_bound({l,l,0}); for(;it!=s.end() && (*it).r<=r;++it) ((*it).sum += val)%=mod; } inline void copy(int la,int ra,int lb,int rb) { split(la,ra); split(lb,rb); s.erase(s.lower_bound({lb,lb,0}) , s.upper_bound({rb,rb,0})); sto it = s.lower_bound({la,la,0}); for( ; it != s.end() && (*it).r <=ra ;++it) s.insert({lb + (*it).l - la , lb + (*it).r - la , (*it).sum}); } inline void swap(int la,int ra,int lb,int rb) { split(la,ra); split(lb,rb); int lena = 0,lenb = 0; sto ita = s.lower_bound({la,la,0}); sto itb = s.lower_bound({lb,lb,0}); for(;ita!= s.end() && (*ita).r <= ra;ita++) a[++lena] = *ita; for(;itb!= s.end() && (*itb).r <= rb;itb++) b[++lenb] = *itb; s.erase(s.lower_bound({la,la,0}) , s.upper_bound({ra,ra,0})); s.erase(s.lower_bound({lb,lb,0}) , s.upper_bound({rb,rb,0})); for(int i = 1;i<= lenb;i++) s.insert({la + b[i].l - lb , la + b[i].r - lb , b[i].sum}); for(int i=1;i<=lena;i++) s.insert({lb + a[i].l - la , lb + a[i].r - la , a[i].sum}); } inline void reverse(int l,int r) { split(l,r); sto it = s.lower_bound({l,l,0}); int len = 0; for(;it != s.end() && (*it).r <=r ;it++) a[++len] = *it; s.erase(s.lower_bound({l,l,0}) , s.upper_bound({r,r,0})); for(int i=len;i;i--) s.insert({l + r - a[i].r , r - a[i].l + l , a[i].sum}); } inline void write() { for(sto i=s.begin();i!=s.end();i++) { int l,r,c; l = (*i).l; r = (*i).r; c = (*i).sum; for(int j=l;j<=r;j++) printf("%lld ",c); } printf("\n"); } signed main() { int n,q; read(n); read(q); for(int i=1,x;i<=n;i++) { read(x); s.insert({i,i,x}); } for(int i=1,opt,l,r,la,lb,ra,rb,c;i<=q;i++) { read(opt); switch(opt) { case 1:read(l);read(r);cout<<query(l,r)<<'\n';break; case 2:read(l);read(r);read(c);assign(l,r,c);break; case 3:read(l);read(r);read(c);add(l,r,c);break; case 4:read(la);read(ra);read(lb);read(rb);copy(la,ra,lb,rb);break; case 5:read(la);read(ra);read(lb);read(rb);swap(la,ra,lb,rb);break; case 6:read(l);read(r);reverse(l,r);break; } } write(); }
\(End\)
珂朵莉樹\(set\)實現的複雜度是\(O(nloglogn)\)
如果您足夠閒可以考慮嘗試連結串列實現珂朵莉樹——複雜度\(O(nlogn)\)
雖然\(set\)的複雜度更優 , 但考慮到頻繁呼叫\(STL\),所以兩者在正常規模下應該是五五開吧
推薦一道珂朵莉樹水題