BZOJ 3223 文藝平衡樹(區間翻轉)

2021-09-10 10:08:57 字數 1444 閱讀 6181

給定n個數,對這個數列進行m次操作,每次將乙個選定區間[l, r]翻轉。求出m次操作後得到的數列。

splay

這應該算是splay的經典應用了吧。

翻轉本身不算難事:對於乙個帶翻轉的區間[l, r],先將l-1旋轉至樹根,再將r+1旋轉至根的有節點。這樣一來,[l,r]的節點全部都在r+1的左兒子處了。

於是,我們給每個節點乙個tag,表示這棵子樹下的區間是否需要翻轉。訪問到這個節點時,將標記下放,並交換左右子樹就行了。

看到這裡,你或許會想:交換左右子樹?這不就把排序二叉樹的特性給破壞了嗎?

所以,我們並不將值a[i]塞到節點裡,而是直接用值最開始時的下標i來作為節點的值。接下來講一下原理:

在之前敲splay的時候,我們一般像這樣儲存splay:

struct nodet[max_n];
我們為了討論方便,將它稱作「樹表」。

回想一下splay操作:

inline void rorate(int x)

inline void splay(int x, int to)

if(!to)

root = to; //root表示根

}

我們發現:不管我們如何對splay進行操作,只是樹的層次關係發生變化,但這個節點在原樹表的絕對位置始終不變。

所以,我們可以這樣構建一顆完美的樹:把原序列的中點拎起來,將序列分成兩部分,然後對於這兩部分遞迴處理即可。

這樣一來,左右節點的含義發生了改變:左子樹表示在現在的數列中在mid左邊的元素,右子樹的含義同理,交換子樹時,就相當於將[l,mid-1]與[mid+1,r]交換位置,這樣就完成了一次區間翻轉。

#include#include#includeusing namespace std;

const int mn = 100005, inf = 1 << 30;

struct nodet[mn];

int a[mn], root;

bool flag;

inline void update(int s)

inline void push_down(int s)//懶標記下傳

int make_tree(int l, int r, int f)

inline void rorate(int x)

inline void splay(int x, int to)

if(!to)

root = x;

}inline int get_num(int x)

}inline void rev(int l, int r)

void print(int s)

print(t[s].ch[1]);

}int main()

print(root);

}

BZOJ 3223 文藝平衡樹

您需要寫一種資料結構 可參考題目標題 來維護乙個有序數列,其中需要提供以下操作 翻轉乙個區間,例如原有序序列是5 4 3 2 1,翻轉區間是 2,4 的話,結果是5 2 3 4 1 n,m 100000 splay拿來練翻轉 這題就只用支援翻轉,很裸 翻轉區間 x,y 的操作 提取該區間,打乙個bo...

bzoj3223 文藝平衡樹

bzoj3223 文藝平衡樹 description 您需要寫一種資料結構 可參考題目標題 來維護乙個有序數列,其中需要提供以下操作 翻轉乙個區間,例如原有序序列是5 4 3 2 1,翻轉區間是 2,4 的話,結果是5 2 3 4 1 input 第一行為n,m n表示初始序列有n個數,這個序列依次...

bzoj 3223 文藝平衡樹

題意 對於乙個1 n的序列。進行m次區間反轉操作 求最後反轉過的區間。n,m 100000。題解 splay躶題。寫完維修數列之後感覺這種題都好寫了。反轉啥的打個標記下傳就好,記得輸出時再pushdown標記就好了 這篇題解就是說一下單旋和雙旋的簡單差別 爺爺結點就是目標的情況不討論了 zig za...