状态表示:f[u][0/1],以u为根节点,有人数偶数(0表示)或奇数个(1表示)的最大权值
这道题其实是状态只有两种的树上背包。
但是第一次做这个题的时候。真的不会想到用树上背包来做,因为他又一个下属必须是偶数的限制条件在那里。就会感觉不能用背包。但没想到用初始化f[u][1]=负无穷,来防止了这种情况。
这点就是这道题的精妙之处。
通常状态下会将f[u][1]=a[u]初始化为该点的权值,f[u][0]=0初始化为空(即0);
但是这样的话,之后转移的时候f[u][0]会从f[u][1](该处为上个状态的f[u][1])+f[v][1]转移过去。
那么这样转移的意义就是选了a[u]且以v为根的子树选了奇数个,这样的话此时u的下属个数就是奇数了。违反题意了。
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxn=2e5+50;
int a[maxn];
int h[maxn];
int n,idx;
struct Edge{
int v,nxt;
}edge[maxn<<1];
LL f[maxn][2];
void add(int u,int v){
edge[idx].v=v;
edge[idx].nxt=h[u];
h[u]=idx++;
}
void dfs(int u){
f[u][1]=-1e18;
for(int i=h[u];~i;i=edge[i].nxt){
int v=edge[i].v;
dfs(v);
LL x=max(f[u][1]+f[v][0],f[u][0]+f[v][1]);
LL y=max(f[u][0]+f[v][0],f[u][1]+f[v][1]);
f[u][1]=x;f[u][0]=y;
}
f[u][1]=max(f[u][0]+a[u],f[u][1]);
}
int main(){
memset(h,-1,sizeof(h));
scanf("%d",&n);
for(int i=1;i<=n;i++){
int fa;
scanf("%d%d",&fa,&a[i]);
if(~fa) add(fa,i);
}
dfs(1);
cout<<f[1][1]<<endl;
return 0;
}