[FJOI2018]领导集团问题
https://www.luogu.org/problemnew/show/P4577
题解
我们可以先搞一个\(dp\)出来,\(dp[u][i]\)表示以\(u\)号节点为根的子树,选择集合元素中最小的不小于\(i\)的最优方案。
然后我们可以发现这个\(dp\)是自带一个后缀\(max\)的,然后我们把它向后差分一下去维护。
那么观察到对于一个节点,它的所有子树之间是互不影响的,所以我们直接把他们对应位置加起来就好了。
然后考虑加入当前的根节点,发现这个\(dp\)值会有一段区间\(+1\),直接在差分数组上维护就好了。
用\(set\)维护差分数组的拐点超级好写。。。
代码
#include<bits/stdc++.h>
#define N 200009
using namespace std;
typedef long long ll;
int head[N],tot,n,w[N];
multiset<int>s[N];
multiset<int>::iterator it;
inline ll rd(){
ll x=0;char c=getchar();bool f=0;
while(!isdigit(c)){if(c=='-')f=1;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return f?-x:x;
}
struct edge{int n,to;}e[N];
inline void add(int u,int v){
e[++tot].n=head[u];e[tot].to=v;head[u]=tot;
}
inline void merge(multiset<int> &u,multiset<int> &v){
if(u.size()<v.size())swap(u,v);
for(it=v.begin();it!=v.end();++it)u.insert(*it);
}
void dfs(int u){
for(int i=head[u];i;i=e[i].n){
int v=e[i].to;
dfs(v);
merge(s[u],s[v]);
s[v].clear();
}
s[u].insert(w[u]);
it=s[u].lower_bound(w[u]);
if(it!=s[u].begin())--it,s[u].erase(it);
}
int main(){
n=rd();
for(int i=1;i<=n;++i)w[i]=rd();
for(int i=2;i<=n;++i){
int x=rd();
add(x,i);
}
dfs(1);
printf("%d",s[1].size());
return 0;
}