BZOJ 4919 大根堆【set啟發式合併 維護樹上LIS】
阿新 • • 發佈:2018-12-14
傳送門 題意: 對於一顆有點權的樹, 如果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]));
}