【题意】一颗有n个节点的树,每个节点有val值和c(有无陷阱)值,给出最大可踩陷阱数m。在树中任取一点作为起点,经过某点就取得该点的val值,踩到第m个陷阱后马上停止,而且不能走已走过的点,求所能获得的最大val总值。

【解题方法】树形dp,经典题,虽然我不会。 dp[u][i][0/1]:=以u为根的子树中,从有无陷阱的节点出发,且经过i个陷阱的最大权值和
我们来考虑下ans的构成,枚举2颗子树接起来,然后就是枚举各自经过的陷阱数,边界情况很多−−
需要注意的是枚举其中一颗子树不能包括另一个子树,树形dp常见技巧了 更新的时候,对于dp[v][j][0]要往

对于dp[v][j][1]要往dp[u][j+trap[u][1]更新的话,j可以=c,因为dp[u][c][1]构成答案的时候,可以作为终点

【AC code】

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int inf=0x3f3f3f3f;
const int maxn=1e5+10;
int n,c,val[maxn],trap[maxn];
ll dp[maxn][5][2];//u为根子树,经过j陷阱,起点有(0)无(1)陷阱
vector<int>G[maxn];
ll ans;
void getmax(ll &a,ll b){
    if(b>a) a=b;
}
void dfs(int u,int fa){
    dp[u][trap[u]][trap[u]]=val[u];
    for(int v:G[u]){
        if(v==fa) continue ;
        dfs(v,u);
        for(int i=0; i<=c; i++){
            for(int j=0; i+j<=c; j++){
                getmax(ans,dp[u][i][1]+dp[v][j][1]);
                if(i<c) getmax(ans,dp[u][i][0]+dp[v][j][1]);
                if(j<c) getmax(ans,dp[u][i][1]+dp[v][j][0]);
                if(i+j<c) getmax(ans,dp[u][i][0]+dp[v][j][0]);
            }
        }
        for(int i=0; i<=c; i++){
            if(i<c) getmax(dp[u][i+trap[u]][0],dp[v][i][0]+val[u]);
            getmax(dp[u][i+trap[u]][1],dp[v][i][1]+val[u]);
        }
    }
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--){
        scanf("%d%d",&n,&c);
        memset(dp,-0x3f,sizeof(dp));
        for(int i=1; i<=n; i++) G[i].clear();
        for(int i=1; i<=n; i++){
            scanf("%d%d",&val[i],&trap[i]);
        }
        int u,v;
        for(int i=1; i<n; i++){
            scanf("%d%d",&u,&v);
            G[++u].push_back(++v);
            G[v].push_back(u);
        }
        ans=0;
        dfs(1,-1);
        printf("%I64d\n",ans);
    }
}