【思路分析】

比赛的时候想到了用我确实也想到了树形DP,但是状态没有确定对,连样例都没有过
PS:这是第二道发现还可以用状态作为答案最后输出的题目

正解:树形DP(背包)

按照读进来的数据,我们先建一棵树
像这样(这里用vector存图)

  for(int i=1;i<=n;++i){
        int x=read(),y=read();
        a[i]=x;  v[y].push_back(i);//从父节点建一条边连向子节点
    }

然后就是DP的过程
(本人见到的树形DP题目比较少,但是做到过相关的题目似乎都是想这样子的)

void dfs(int x){
    ***********************//这里写初始化,或者边界判断
    for(int i=0;i<v[x].size();i++){
        int nxt=v[x][i];
        dfs(nxt);//先处理它的子树
        ************************//这里写转移方程
    }
    ***************************//这里进行后续操作
}

确定了是树形dp之后,我们来想一下转移方程

f[i][j][1]表示i节点分配j个亲缘关系取后最多的产奶量

f[i][j][0]表示该节点分配j个亲缘关系不取后最多的产奶量

然后像背包一样

我们给第i个子树分配j个关系

可以得出方程

方程:

(未加初始化与后续处理)
f[x][j][0]=max(f[x][j][0],f[x][j-k][0]+max(f[nxt][k][0],f[nxt][k][1])),
f[x][j][1]=max(f[x][j][1],f[x][j-k][1]+max(k==0?-0x3f3f3f3f:f[nxt][k-1][1],f[nxt][k][0]));

AC代码大概是这样的:

代码:

#include<cstdio>
#include<cmath>
#include<cctype>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<vector>
#include<map>
#include<set>
using namespace std;
inline int read()
{
    char chr=getchar();
    int f=1,ans=0;
    while(!isdigit(chr)) {if(chr=='-') f=-1;chr=getchar();}
    while(isdigit(chr))  {ans=(ans<<1)+(ans<<3)+chr-'0';chr=getchar();}
    return ans*f;
}

inline void kai()
{
    freopen("d.in","r",stdin);
    freopen("d.out","w",stdout);
}
int n,m,a[505];
vector<int> v[505];
int f[505][505][2];
void dfs(int x){
    for(int i=1;i<=n;i++) f[x][i][0]=f[x][i][1]=-0x3f3f3f3f;//预处理
    for(int i=0;i<v[x].size();i++){
        int nxt=v[x][i];
        dfs(nxt);//遍历子树
        for(int j=n;j>=0;j--)
            for(int k=0;k<=j;k++)
            f[x][j][0]=max(f[x][j][0],f[x][j-k][0]+max(f[nxt][k][0],f[nxt][k][1])),
            f[x][j][1]=max(f[x][j][1],f[x][j-k][1]+max(k==0?-0x3f3f3f3f:f[nxt][k-1][1],f[nxt][k][0]));//方程
    }
    for(int i=0;i<=n;i++) f[x][i][1]+=a[x];//后续处理
}
int ans=0,maxn=0;
int main(){
    n=read();
    m=read();
    for(int i=1;i<=n;++i){
        int x=read(),y=read();
        a[i]=x;  v[y].push_back(i);//建树
    }
    dfs(0);//建一个“超级节点”(所有没有祖先的根节点都连到了0上)
    int i;
    for(i=n;i;i--) if(f[0][i][0]>=m) break;//用状态来做答案,找到第一个产奶量最多的亲缘关系(倒续找,所以最先找到的就是最大的)
    cout<<i;
    return 0;
}