NOI2004 鬱悶的出納員 Splay

2021-10-02 11:39:56 字數 2141 閱讀 3244

題目描述

首先這題一看就是道平衡樹。我們來考慮如何用平衡樹實現這個操作

首先,如果要給員工加工資或者扣工資,平衡樹肯定是實現不了。我們要用乙個變數來記錄目前加了多少工資,我們可以先叫它laz

ylazy

lazy

,我們每個詢問在取出的時候加上laz

ylazy

lazy

就好了。

但是如果乙個員工是新加入公司的,那麼前面給員工加的工資它肯定是享受不到,為了解決這個問題,我們在每個員工加入時,他的工資要減去laz

ylazy

lazy

這個問題解決了,再結合平衡樹的一些知識,這道題就差不多了。我們來總結一下這裡的操作怎麼實現:

加入員工:直接在平衡樹里加入乙個節點即可。

加工資:直接 laz

y+=k

lazy += k

lazy+=

k 即可

扣工資:首先 laz

y−=k

lazy -= k

lazy−=

k 然後我們在平衡樹中插入乙個節點 laz

y−mi

nn

lazy - minn

lazy−m

inn(minn是工資下界,也就意味著比這個工資低的都要刪除)

我們將這個點旋轉到根,並直接將根的左子樹全部殲滅(將根的左兒子設為0)

第k大:基本平衡樹操作

貼個**`

#include

using

namespace std;

const

int n =

1e5+10;

#define ll long long

#define for(i,a,b) for(int i = a;i <= b;i++)

#define _for(i,a,b) for(int i = a;i >= b;i--)

template

<

typename t>

void

read

(t &x)

int n,minn;

int root,tot;

//平衡樹的根,以及目前有幾個節點

int fa[n]

,son[n][2

],val[n]

,cnt[n]

,size[n]

;//節點的爸爸,節點的兩個兒子,節點的值,節點數字的數量 ,子樹的大小

intf

(int x)

//判斷乙個節點是左兒子還是右兒子

void

pushup

(int x)

//及時更新子樹大小

}void

rotate

(int x)

void

splay

(int x,

int qaq)

//將x旋轉到y的兒子 if(

!qaq) root = x;

}void

ins(

int x)

int u = root,fath =0;

//從根節點往下走

while(1

) fath = u,u = son[u]

[x > val[u]];

if(!u)//新建節點 }}

intfind

(int x)

//尋找x,如果f = 1表示要將他旋轉到根

intpre_next

(int x,

int f)

//0表示找前驅,1表示找後繼

void

delete

(int x)

intkth

(int x)}}

intrank

(int x)

res +

= cnt[u]

; u = son[u][1

];}}

}int

main()

if(op[0]

=='f')}

ans -

=rank

(1e9)-

2;printf

("%d\n"

,ans)

;return0;

}

noi 2004 鬱悶的出納員

原題位址 花了一兩天真正的熟悉了treap,對於乙個東西,本蒟蒻認為,不應該要會,還應會熟練的寫,像哈狗寫這個只需十分鐘 好吧,話歸正題 先推薦另類解法 戳進去 此題解法很多bit 權值線段樹 各種平衡樹 準備抽空寫 是一道很棒的模板題。這道題我們把每次全體加的工資和減的弄到乙個變數,姑且叫為w,把...

NOI2004 鬱悶的出納員

大致題意就是對初始為空的數列的各種操作233 題解 splay啦 其中可能會遇到的幾個問題 1.如何刪去低於min的點 找到數列中min的後繼提為根,把根的左孩子丟 shan 了就完成了 很容易yy到的對吧 2.如何處理對當前數列中的數進行 k 開個全域性變數存整體的波動 值ff。那麼刪點的時候就把...

NOI2004 鬱悶的出納員

點此看題 這道題很新的一點就是加工資和減工資,這貌似要用到區間修改。但是細細想想,操作的物件是全樹,我們可以維護乙個累積的修改值,新加入乙個點的話要刪去之前的累計的修改值,對於減工資的情況,判斷有沒有低於最低的限制。注意如果乙個一來人就低於最低線,那麼這個人不被統計在最後的刪除人數中。include...