1. 程式人生 > >BZOJ 4919 大根堆【set啟發式合併 維護樹上LIS】

BZOJ 4919 大根堆【set啟發式合併 維護樹上LIS】

傳送門 題意: 對於一顆有點權的樹, 如果i是j的祖先, 那麼要滿足vi > vj, 問最多可以在這棵樹上選擇多少個點可以滿足這個條件.

思路: 那麼這就是樹上lis,怎麼維護了, 每個點依舊像普通陣列這樣, 那麼就有合併, 要合併某個點和兒子的數字, 然後依舊還是g[i]表示lis長度為i時, 最小的a[i]可以是多少, 那麼直接就查lower_bound就行, 又要排序, 和重值, 所以我們就用multiset就可以很好的維護了, 合併時記得啟發式合併就行.

AC Code

const int maxn = 2e5+5;
vector<int>g[maxn];
multiset<
int>st[maxn]; int a[maxn]; void Un(int x, int y) { if (sz(st[x]) < sz(st[y])) { swap(st[x], st[y]); } multiset<int>::iterator it = st[y].begin(); for (; it != st[y].end(); it++) { st[x].insert(*it); } } void dfs(int u) { for (int i = 0 ; i < sz(g[
u]) ; i ++) { dfs(g[u][i]); Un(u, g[u][i]); } multiset<int>::iterator it = st[u].lower_bound(a[u]); if (it != st[u].end()) { st[u].erase(it); } st[u].insert(a[u]); } void solve() { int n; scanf("%d", &n); for (int i = 1 ; i <= n ; i ++
) { int x; scanf("%d%d", a+i, &x); if (x) { g[x].pb(i); } } dfs(1); printf("%d\n", sz(st[1])); }