Description
给定一棵n个节点的有根树,编号依次为1到n,其中1号点为根节点。每个点有一个权值v_i。
你需要将这棵树转化成一个大根堆。确切地说,你需要选择尽可能多的节点,满足大根堆的性质:对于任意两个点i,j,如果i在树上是j的祖先,那么v_i>v_j。
请计算可选的最多的点数,注意这些点不必形成这棵树的一个连通子树。
题解:
好题。考虑一条链的情况,就是求自底向上求最长上升子序列,其实这种思路也可以推广到树上,具体的就是每个节点用一个multiset维护求最长上升子序列时的ff数组,的定义为选了ii个点,最大的点最小是多少,然后对于每个节点用启发式合并把儿子的multiset合并起来,最后加入自己的即可。最后根节点的即为答案。
代码:
#include<cstdio>
#include<cstring>
#include<set>
#include<iostream>
#include<algorithm>
using namespace std;
#define LL long long
const int Maxn=200010;
struct Node{int x,id;}b[Maxn];
bool cmp(Node a,Node b){return a.x<b.x;}
int n,val[Maxn];
struct Edge{int y,next;}e[Maxn];
int last[Maxn],len=0;
void ins(int x,int y){int t=++len;e[t].y=y;e[t].next=last[x];last[x]=t;}
int cnt=0;
multiset<int>S[Maxn];
void dfs(int x)
{
for(int i=last[x];i;i=e[i].next)
{
int y=e[i].y;
dfs(y);
if(S[y].size()>S[x].size())swap(S[x],S[y]);
for(set<int>::iterator j=S[y].begin();j!=S[y].end();j++)
S[x].insert(*j);
}
if(S[x].size()>0&&S[x].lower_bound(val[x])!=S[x].end())S[x].erase(S[x].lower_bound(val[x]));
S[x].insert(val[x]);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++)
{
int fa;
scanf("%d%d",&b[i].x,&fa);b[i].id=i;
ins(fa,i);
}
sort(b+1,b+1+n,cmp);
b[0].x=-1;
for(int i=1;i<=n;i++)val[b[i].id]=(cnt+=((b[i].x==b[i-1].x)?0:1));
dfs(1);
printf("%d",S[1].size());
}