[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;
}
posted @ 2019-05-01 07:18  comld  阅读(210)  评论(0编辑  收藏  举报