先考虑
O(n2)
O
(
n
2
)
的DP,设
fi,j
f
i
,
j
表示考虑子树
i
i
中,选的的最大值
≤j
≤
j
时最多能选多少个点,那么考虑一个点的转移。
先合并儿子:
fi,j=∑x∈sonifx,j
f
i
,
j
=
∑
x
∈
s
o
n
i
f
x
,
j
在选上他自己:
chkmax(fi,≥vi,fi,vi−1+1)
c
h
k
m
a
x
(
f
i
,
≥
v
i
,
f
i
,
v
i
−
1
+
1
)
那么可以用线段树合并+区间覆盖操作搞定。
还有一种基于multiset的做法。先考虑一条链的情况就是求LIS;那么树的情况也类似, x x 点先继承重儿子的multiset,再把其他儿子的multiset合并,再用替换第一个 ≥vx ≥ v x 的元素即可。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<set>
#define N 200010
#define mid (l+r>>1)
using namespace std;
int n,val[N],sz[N],hs[N],id[N],top,z[N];
multiset<int> s[N];
int read()
{
int x=0,f=1;char ch=getchar();
for(;ch<'0'||ch>'9';ch=getchar()) if(ch=='-') f=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*f;
}
struct edge
{
int t;
edge *next;
}*con[N];
void ins(int x,int y)
{
edge *p=new edge;
p->t=y;
p->next=con[x];
con[x]=p;
}
void dfs1(int v)
{
sz[v]=1;
for(edge *p=con[v];p;p=p->next)
{
dfs1(p->t),sz[v]+=sz[p->t];
if(sz[p->t]>sz[hs[v]]) hs[v]=p->t;
}
}
void dfs2(int v)
{
if(hs[v]) dfs2(hs[v]),id[v]=id[hs[v]];
else id[v]=v,s[v].insert(val[v]);
for(edge *p=con[v];p;p=p->next)
if(p->t!=hs[v])
{
dfs2(p->t);
s[id[v]].insert(s[id[p->t]].begin(),s[id[p->t]].end());
s[id[p->t]].clear();
}
multiset<int>::iterator it=s[id[v]].lower_bound(val[v]);
if(it!=s[id[v]].end())s[id[v]].erase(it);
s[id[v]].insert(val[v]);
}
int main()
{
n=read();
for(int i=1;i<=n;i++)
{
val[i]=read();
int x=read();
if(x) ins(x,i);
}
dfs1(1);
dfs2(1);
printf("%d",s[id[1]].size());
return 0;
}