HNOI2002 營業額統計 Treap

2022-05-01 23:15:11 字數 2641 閱讀 1521

[hnoi2002]營業額統計

題目大意:給你一串n數序列,對於每乙個剛輸入的數a,找到乙個前面的數k,使得|a-k|最小。

注釋:$n<=32767,ai<=10^6$。

想法:剛學treap。這道算treap的練習題裡吧,對於新手來講還是挺有意義的。首先,我們先來講一講treap是個什麼東西。

在這之前,我們宣告:

struct node

void push(int k)//push代表將子樹的資訊更新至根節點

treap,是二叉搜尋樹(tree)和堆(heap)的合體,讀音也是這兩個東西捏在一起的(懵-)。我們來看一下這東西是怎麼實現的。

treap是一種二叉搜尋樹,但也是一種*衡樹。我們顯然知道,對於二叉搜尋樹來講,如果插入的順序是直接公升序或者降序,二叉搜尋樹就會退化成一條鏈,查詢和修改還有刪除的時間複雜度都會從原本的期望logn滾粗會o(n)。這就很蛋疼,而且卡二叉搜尋樹的題有不少,那麼我們可以用一種什麼樣的辦法來使得二叉搜尋樹趨*於*衡的呢?這是乙個問題。然後我們再來看另乙個問題。

關於heap來講,公升序或者降序對於堆的傷害顯然是... ...沒有的。我們嘗試用heap將二叉搜尋樹進行一定的改造。之前的二叉搜尋樹的節點維護的只是權值,現在,我們讓它在多乙個隨機的rand。這樣的好處是什麼?就是可以用這個隨機的rand來使得存在乙個樹,滿足這個樹對於原本二叉搜尋樹的權值來講是滿足二叉搜尋樹的,對於rand來講是滿足heap的(這裡是大根堆還是小根堆是無所謂的)。如何來呈現一顆這樣的樹呢?我們通過旋轉treap的核心操作——旋轉。如何進行旋轉?首先,我們最主要的目的就在保證二叉搜尋樹的前提下完成對堆的構建。那麼,我們來考慮在如何不改變原有二叉搜尋樹的前提下旋轉堆。首先,二叉搜尋樹的大小排序是由中序遍歷決定的,而堆的大小排序是由深度決定的。我們可以通過這樣的操作:

void lturn(int &k)//向左旋轉

沒錯,就是這樣可以達到我們想要的效果。至於**...菜逼的我並不會怎麼把圖粘上來,所以自己在紙上推演即可。然後,附上一些版子,知道了treap的旋轉之後就很好理解了。說一說插入,在插入節點時,先遞迴處理插入符合二叉搜尋樹的權值,然後取rand,通過遞迴旋轉達到同時滿足tree和heap的神奇的樹即可。

void update(int &k,int temp)

a[k].size++;

if(temp==a[k].val) a[k].num++;

else if(temp刪除和插入的原理是大致相同的,不在贅述。向說一說找前驅和後繼的事情。我是照著lijinnn的版子敲的,所以找前驅和後繼是遞迴的,edwardfrog說可以不遞迴,推薦看一下,在此貼遞迴版子前驅

void ask_before(int k,int temp)

else ask_before(a[k].lson,temp);

}

然後,說一下這道題。由於求的是最小波動,我們考慮什麼樣的數在treap上和當前輸入的數的波動是最小的?顯然,是前驅或者是後繼即可。所以,我們只需要找到對應的前驅、後繼,然後對於波動的絕對值取min即可。

最後,附上醜陋的**... ...

#include #include #include //rand的時候會用到

using namespace std;

int befor,aftr;

int tot;

int root;

// int v[1000010];

int abs(int x)//建議手寫abs

struct node

a[50010];

void push(int k)//更新節點資訊

void lturn(int &k)//向左旋轉

void rturn(int &k)//向右旋轉

void update(int &k,int temp)//在以k為根節點的樹內插入乙個權值為temp的節點

a[k].size++;//顯然,在以k為根節點的子樹中新增節點,k的size必須+1

if(temp==a[k].val) a[k].num++;

else if(tempa[k].val)

// return a[a[k].lson].size+a[k].num+ask_rank(a[k].rson,temp);

// else return ask_rank(a[k].lson,temp);

// }

void ask_before(int k,int temp)//前驅

else ask_before(a[k].lson,temp);

}void ask_after(int k,int temp)//後繼

else ask_after(a[k].rson,temp);

}int main()

printf("%d\n",all);

}

小結:學到了乙個超級強大的資料結構啊!!!。

錯誤:1.之前的做法是取rank,我們要明白,節點的編號對於treap來講,只有在對整個treap進行修改時才有用,對於使用treap來講用處是極其小的。

2.update的!k並不是return,我們要明白update的最核心的一句話其實是第一句。只有在!k的時候才有可能對於整個treap進行形式上的增加節點的操作(有可能之前有重複)。

HNOI2002 營業額統計

花了一天鑽研了splay,然後發現splay沒我想象的那麼難 以前都是寫sbt來著 但是splay的速度確實沒那麼快,但是真的挺好寫的 我寫的版本測了這題以後又用了一下別人的splay,發現通過這題大多數splay在750ms上下,我是688ms,說明還算是不錯的啦 啦啦啦 include incl...

HNOI 2002 營業額統計

最近開始重新學習splay樹寫的第一題,基本就是照著別人部落格改的一道題,關於splay樹的模板,感覺大牛已經把 改得很短!這道題沒什麼難度,乙個插入操作,乙個找前驅,乙個找後驅的操作。話說這題有個資料有個bug的地方,可以看連線的discuss 因為沒有push down,push up的操作,感...

HNOI2002 營業額統計

傳送門 題目大意 求一段序列,小於當前元素的最大值和大於當前元素的最小值。從該元素前面的元素找。題解 建立線段樹維護或者使用雙向鍊錶.或stl水過 線段樹每次插入乙個新值,查詢大於它的最小值和小於它的最大值 雙向鍊錶有點神.我們知道排序後乙個數的前驅就是小於它的最大值 後繼就是大於它的最小值,我們將...