九省聯考2018 IIIDX 貪心 線段樹

2022-04-29 21:54:12 字數 3024 閱讀 4499

~~~題面~~~

題解:首先有乙個比較明顯的50分貪心:

先把d排好序,然後按從小到大的順序貪心的給每個點選值,同等條件下優先編號大的,於是越小的值會越趨近於放在編號越大的上面。

但是這樣在數字重複的情況下是不對的, 比如下面這組資料:

4 3.0

1 1 2 2

貪心會得到1 1 2 2 ,而正確答案是1 2 2 1.

因此換個角度考慮,在什麼情況下乙個點可以取到乙個值x?

設這個點的子樹大小為size[i],那麼這個點可以取到值x,當且僅當大於等於x的還沒被取的值的個數 >= size[i],原因應該是很好理解的,畢竟還要有size[i] - 1和比x大的值放在i的子樹上才行。

因此我們先對d從小到大排序去重,統計每個值的出現次數cnt[x], 然後對於每個數統計乙個f[x]表示大於等於x的還沒被取的值的個數為f[x].

假設我們給節點i匹配上了乙個值x,那麼這個策決策對小於等於x的值的影響是確定的,因此將所有小於等於x的值的f陣列都減小size[i],表示這些值的右邊可以取的值又減小了size[i]個。

我們將和這個操作稱為「預定」,因為我們現在並不知道點i的子樹分別會選擇哪些值,但我們知道它們要選幾個值,所以我們相當於先告訴後面的人,這個節點i已經坐到了x這個值上,並且要求後面的人為它的子樹留size[i] - 1個座位。

因為這個決策對大於x的值的影響是不確定的,我們暫時沒有修改它,但其實這個決策會對它產生影響,那麼對於乙個值k,在取它之前的決策到底對它產生了什麼樣的影響呢?

對於乙個值k,它的真正的f[k]其實是min(f[1], f[2], f[3] ....f[k]),因為每個f值都相當於乙個字尾和,乙個合法的值不能使得f[k]反而比它前面的f值更大,因為前面的f值要比f[k]統計了更多的數。

因為題目要求使得字典序最大,所以我們按照編號從小到大給節點分配值顯然是最優的。

因此我們每次就是要在剩下的數上尋找乙個最靠右的,並且使得min(f[1], f[2], f[3] ...f[k]) >= size[i]的k。

因為涉及區間修改和查詢,我們使用線段樹來維護所有的f值,每次選好乙個值,我們就把一些已經確定的影響從線段樹中刪除(對乙個區間進行- size[i]的操作)。

值得注意的是,在每次查詢前,如果乙個節點的父親的影響還沒有被撤銷的話,應該要撤銷它父親的影響。(即把父親給子樹佔的座給加回來 :1 ~ 父親匹配值的f值 加上 size[fa] - 1)

為什麼呢?

想象一下,如果不撤銷父親的影響的話,豈不是相當於別人特意給你佔了座,結果你自己還不能坐上去?

因為乙個節點的兒子都是連續的,所以我們在撤銷的父親的影響後,會馬上把不應該撤銷的部分給補上(兒子的子樹在不斷的加上來),所以不用擔心對之後的決策造成影響。

1 #include2

using

namespace

std;

3#define r register int

4#define ac 551000

5#define ac 250000067

intn, w, go, tot, rnt;

8doublek;9

int ans[ac], cnt[ac], father[ac], size[ac], f[ac], s[ac];//

cnt[i]表示排名第i位的d值出現的次數

10bool

done[ac];

1112 inline int

read()

1319

20 inline int min(int a, int

b)23

24struct

seg_tree

3031 inline void pushdown(int

x)3240}

4142

void build(int x, int ll, int

rr)43

46int mid = (ll + rr) >> 1

;47 build(x * 2, ll, mid), build(x * 2 + 1, mid + 1

, rr);

48update(x);49}

5051

void change(int x, int ll, int

rr)52

59int mid = (l[x] + r[x]) >> 1;60

if(rr <= mid) change(x * 2

, ll, rr);

61else

if(ll > mid) change(x * 2 + 1

, ll, rr);

62else change(x * 2, ll, mid), change(x * 2 + 1, mid + 1

, rr);

63update(x);64}

6566

void find(int

x)67

70if(tree[x * 2] >= w) find(x * 2 + 1

);71

else find(x * 2

); 72

update(x);73}

74}t;

7576

void

pre()

7786

for(r i = tot; i; i --)

87 f[i] = f[i + 1] + cnt[i];//

存下每個值右邊有多少個可供選擇的值

88for(r i = n; i; i --)

89 father[i] = floor(i / k), size[father[i]] += size[i];//

獲取每個節點的size

90 t.build(1, 1

, tot);91}

9293

void

work()

94105 printf("\n"

);106

}107

108int

main()

109

view code

九省聯考2018 IIIDX 貪心

題目背景 osu聽過沒?那是konano最喜歡的一款 遊戲,而他的夢想就是有一天自己也能做個獨特酷炫的 遊戲。現在 他在世界知名遊戲公司konmai內工作,離他的夢想也越來越近了。這款 遊戲內一般都包含了許多歌曲,歌曲 越多,玩家越不易玩膩。同時,為了使玩家在遊戲上氪更多的金錢花更多的時間,遊戲一開...

九省聯考2018 IIIDX 線段樹 貪心

給出 k 和 n 個數,構造乙個序列使得 d i d i k 並且字典序最大。聽說,當年省選的時候,這道題擋住了大批的高手,看上去十分簡單,實際上那道彎段時間內是轉不過來的。首先,乙個套路是,將這個序列的關係抽象成一棵樹,i的父親是floor i k 我們要要求子樹內部的點的權值都比父親大。我們觀察...

2472 九省聯考 2018 IIIDX

一眼思路的題 就是比較難寫.考慮乙個點必須小於其 i dk id k 那麼容易想出乙個樹形結構,每個點都大於其父親.那麼對於乙個點,那麼他能選取的最大值就是當前能選的所有點中的n size id n s ize id 這個點的值。然後留夠其兒子的位置即可。最後如果有相同的點,容易想到把當前點放在權值...