这也能dp??
不dp也没法做了
设sz[x]是x子树中叶子节点的个数,f[x]是把1 ~ sz[x]这个排列放入到x子树的叶子节点上,x节点能取到的最大值
当x为叶子时,
当x不是叶子时,,f[x]就得分别考虑取最大值和取最小值的情况
1)当x取最大值时,考虑某个儿子v对x的转移,对于一个儿子v,它要占掉sz[v]-f[v]个值,也就是说向其中放入sz[v]个数,从小到大后sz[v]-f[v]个数是x取不到的
那么这时候就发现f[x]的取值只与某个儿子v有关,也就是把1~sz[x]从小到大后sz[v]个数放入到v子树中,这时x在v上取的值是sz[x]-(sz[v]-f[v])
所以得到转移方程
2)当x取最小值时,就得需要考虑所有的儿子从而保证取得最小值够大,那么怎么考虑呢
正如上述所说,对于一个儿子v,它要占掉sz[v]-f[v]个值,然后把所有儿子的sz[v]-f[v]都加起来,就得到取不到的值的个数C,这时就从大到小把sz[x]~1填入C个,剩下的就是儿子能取到的数
因为要在儿子中选最小,设S为x的儿子个数,所以还要再往前填S个数,其中填入的第S个数就是f[x]的取值
转移方程
上面的方程手玩几个例子就得到了
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define db double
#define rep(x,a,b) for(int x=(a);x<=(b);x++)
#define per(x,a,b) for(int x=(a);x>=(b);x--)
#define reP(x,a,b) for(int x=(a);x<(b);x++)
#define Per(x,a,b) for(int x=(a);x>(b);x--)
#define scf(a) scanf("%d",&a)
#define scfll(a) scanf("%lld",&a)
#define scfdb(a) scanf("%lf",&a)
#define ptf(a) printf("%d",a)
#define ptfll(a) printf("%lld",a)
#define ptfsp(a) printf("%d ",a)
#define ptfllsp(a) printf("%lld ",a)
#define pli(a,b) make_pair(a,b)
#define pb push_back
#define el puts("")
#define FS first
#define SE second
#define PI acos(-1)
/*ios::sync_with_stdio(false);
freopen("in.txt","r",stdin);
freopen("1.out","w",stdout);*/
using namespace std;
const ll mod=998244353;
const int maxn=3e5+5;
int f[maxn],sz[maxn],d[maxn],a[maxn];
vector<int>g[maxn];
void dfs(int x,int fa){
if(d[x]==1){
sz[x]=1;
f[x]=1;
return;
}
reP(i,0,g[x].size()){
int v=g[x][i];
if(v==fa) continue;
dfs(v,x);
sz[x]+=sz[v];
}
if(a[x]==1){
reP(i,0,g[x].size()){
int v=g[x][i];
if(v==fa) continue;
f[x]=max(f[x],sz[x]-(sz[v]-f[v]));
}
}
else{
int sum=0;
reP(i,0,g[x].size()){
int v=g[x][i];
if(v==fa) continue;
sum+=(sz[v]-f[v]+1);
}
f[x]=sz[x]-sum+1;
}
}
int main(){
int n;scf(n);
rep(i,1,n) scf(a[i]);
rep(i,2,n){
int x;scf(x);
g[x].push_back(i);
g[i].push_back(x);
d[i]++;d[x]++;
}
dfs(1,0);
cout<<f[1];
}