bzoj4919 大根堆 [啟發式合併]
阿新 • • 發佈:2019-01-28
Description:
一棵樹,每個點有權值,要求選出最多的點,並且滿足每個被選的點子樹中沒有權值大於等於該點權值。
Solution:
吐槽:
考試怒剛,結果沒調出來,看到覺得是線段樹合併之類的題,感覺寫不出來。考試後也寫了一個線段樹合併,由於第一次寫所以調了很長時間沒調出來,於是寫了這個版本。
本質上是求樹上,於是我們考慮中維護的陣列表示長度為的結尾的最小值,用維護。每次啟發式合併,並且用當前點的值替換第一個大於等於該值的值,最後輸出根節點的 即可。
元素的相對位置也就是陣列的下標,那麼也就是最長的dp值。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
int n;
int val[maxn];
vector<int> G[maxn];
multiset<int> s[maxn];
multiset<int> :: iterator it;
void merge(int u, int v) {
if(s[u].size() < s[v].size()) {
swap(s[u], s[v]);
}
for (it = s[v].begin(); it != s[v].end(); ++it) {
s[u].insert(*it);
}
s[v].clear();
}
void dfs(int u) {
for(int i = 0; i < G[u].size(); ++i) {
int v = G[u][i];
dfs(v);
merge(u, v);
}
it = s[u].lower_bound(val[u]);
if(it != s[u].end()) {
s[u].erase(it);
}
s[u].insert(val[u]);
}
int main() {
scanf("%d", &n);
for(int i = 1; i <= n; ++i) {
int x;
scanf("%d%d", &val[i], &x);
if(x) {
G[x].push_back(i);
}
}
dfs(1);
printf("%d\n", s[1].size());
return 0;
}