1. 程式人生 > 其它 >珂朵莉樹學習筆記

珂朵莉樹學習筆記

一個神奇的資料結構 , 當資料隨機情況下可以亂殺

又名珂朵莉樹

基本概念與條件性

利用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\),所以兩者在正常規模下應該是五五開吧

推薦一道珂朵莉樹水題