給定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操作:
我們發現:不管我們如何對splay進行操作,只是樹的層次關係發生變化,但這個節點在原樹表的絕對位置始終不變。inline void rorate(int x)
inline void splay(int x, int to)
if(!to)
root = to; //root表示根
}
所以,我們可以這樣構建一顆完美的樹:把原序列的中點拎起來,將序列分成兩部分,然後對於這兩部分遞迴處理即可。
這樣一來,左右節點的含義發生了改變:左子樹表示在現在的數列中在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...