~~~題面~~~
題解:首先有乙個比較明顯的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 #include2view codeusing
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
九省聯考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 這個點的值。然後留夠其兒子的位置即可。最後如果有相同的點,容易想到把當前點放在權值...