Description
给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点。每个点有一个权值v_i。
你需要将这棵树转化成一个大根堆。确切地说,你需要选择尽可能多的节点,满足大根堆的性质:对于任意两个点i,j,如果i在树上是j的祖先,那么v_i>v_j。
请计算可选的最多的点数,注意这些点不必形成这棵树的一个连通子树。
Input
第一行包含一个正整数n(1<=n<=200000),表示节点的个数。
接下来n行,每行两个整数v_i,p_i(0<=v_i<=10^9,1<=p_i < i,p_1=0),表示每个节点的权值与父亲。
Output
输出一行一个正整数,即最多的点数。
Sample Input
6
3 0
1 1
2 1
3 1
4 1
5 1
Sample Output
5
题解
神..
先考虑链上的过程,发现其实就是一个LIS..
又由于子树互不影响,所以可以是多个LIS
先介绍一下LIS的multiset求法
我们从前往后插入数进set中,这个数的位置就是以他为结尾的LIS长度
模拟这个LIS的过程
把子树的multiset与自身合并完之后,找第一个比cal[x]大的值的位置
如果有,直接把cal[x]替换掉这个数
否则在末尾插入cal[x]
上个启发式合并就优秀了
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<set>
using namespace std;
multiset<int> li[200005];
multiset<int>::iterator it;
struct node{int x,y,next;}a[210000];int len,last[200005];
void ins(int x,int y){len++;a[len].x=x;a[len].y=y;a[len].next=last[x];last[x]=len;}
void merge(int u,int v)
{
if(li[u].size()<li[v].size())swap(li[u],li[v]);
for(it=li[v].begin();it!=li[v].end();it++)li[u].insert(*it);
li[v].clear();
}
int cal[200005];
void dfs(int x)
{
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;dfs(y);
merge(x,y);
}
it=li[x].lower_bound(cal[x]);
if(it!=li[x].end())li[x].erase(it);
li[x].insert(cal[x]);
}
int n;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int f;scanf("%d%d",&cal[i],&f);
if(f)ins(f,i);
}
dfs(1);
printf("%d\n",li[1].size());
return 0;
}