The Hanged Man
sol
题意大致就是对于每一个 \(i\in[1,m]\) 求树上独立集 \(S\) 满足 \(\sum\limits_{i\in S}a_i\le m\) 且 \(\sum\limits_{i\in S}b_i\) 最大的 \(S\) 个数。
首先有个很显然的暴力树型 DP,是 \(O(nm^2)\) 的。
考虑优化这个东西,暴力的复杂度问题显然在 \(m^2\),这是由于要合并子树背包,然而单次插入一个值是 \(O(m)\) 的。因此,考虑按 dfs 序顺序背包。
这样的话,各个点能否加入背包就只与其的父节点有关,我么需要知道各个点的父节点的状态,直接状压这个东西是 \(O(2^n)\) 的,显然很伪。
然而不难发现,当所有子节点都处理过之后,这个父节点实际上就没用了,我们不再需要它的状态。考虑只维护当前点到根节点一路上的节点状态,然而这样仍然会被卡成 \(2^n\)。
再优化一点,如果当前点是其父节点的最后一个处理的儿子,那么其父节点在处理完当前点后已经没用了。如何尽可能地减少当前点到根路径上仍有子节点未被处理的节点数呢?
考虑重链剖分,每个点优先处理轻儿子再处理重儿子,那么从一个轻子树跳到下一个子树时,必然会跳过整条重链,那么整条重链内的点都无需额外记录,那么一个点需要记录的节点就是其到根的路径上经过的所有重链的链头的父节点,这个是 \(\log n\) 级别的。
然后就得到了 \(O(n^2m)\) 的做法。
但这真是太麻烦了,考虑乱搞,优化一下暴力,如果当前状态不存在就不背包。由于子树中存在的状态数为 \(2^{siz}\) 级别,这是一个很有用的优化。然后就过了。后面的代码给的是乱搞做法 qwq
code
const int N=55,M=5005;int n,m;
int a[N],b[N];
vec<int> G[N];ll f[N][M][2],g[N][M][2];
void dfs(int x=1,int fa=0){f[x][a[x]][1]=b[x];f[x][0][0]=0;g[x][a[x]][1]=g[x][0][0]=1;for(auto y:G[x])if(y!=fa){dfs(y,x);per(i,m,0){if(~f[x][i][0])per(j,m-i,0)if(~f[y][j][0]||~f[y][j][1]){int t=max(f[y][j][0],f[y][j][1]);if(f[x][i+j][0]<f[x][i][0]+t||!j){f[x][i+j][0]=f[x][i][0]+t;if(f[y][j][0]==f[y][j][1])g[x][i+j][0]=g[x][i][0]*(g[y][j][0]+g[y][j][1]);else if(f[y][j][0]<f[y][j][1])g[x][i+j][0]=g[x][i][0]*g[y][j][1];else g[x][i+j][0]=g[x][i][0]*g[y][j][0];}else if(f[x][i+j][0]==f[x][i][0]+t){if(f[y][j][0]==f[y][j][1])g[x][i+j][0]+=g[x][i][0]*(g[y][j][0]+g[y][j][1]);else if(f[y][j][0]<f[y][j][1])g[x][i+j][0]+=g[x][i][0]*g[y][j][1];else g[x][i+j][0]+=g[x][i][0]*g[y][j][0];}}if(~f[x][i][1])per(j,m-i,0)if(~f[y][j][0]){if(f[x][i+j][1]<f[x][i][1]+f[y][j][0]||!j){f[x][i+j][1]=f[x][i][1]+f[y][j][0];g[x][i+j][1]=g[x][i][1]*g[y][j][0];}else if(f[x][i+j][1]==f[x][i][1]+f[y][j][0]){g[x][i+j][1]+=g[x][i][1]*g[y][j][0];}}}}
}inline void Main(){cin>>n>>m;rep(i,1,n)cin>>a[i]>>b[i],G[i].clear();rep(i,1,n)rep(j,0,m)rep(k,0,1)f[i][j][k]=-1,g[i][j][k]=0;repl(i,1,n){int x,y;cin>>x>>y;G[x].pub(y),G[y].pub(x);}dfs();rep(i,1,m){if(f[1][i][0]==f[1][i][1])cout<<g[1][i][0]+g[1][i][1]<<" ";else if(f[1][i][0]<f[1][i][1])cout<<g[1][i][1]<<" ";else cout<<g[1][i][0]<<" ";}cout<<'\n';
}
int main(){// ReadFile(___);#define MultiTasksios::sync_with_stdio(false);cin.tie(0);cout.tie(0);#ifdef MultiTasksint t;cin>>t;rep(i,1,t)cout<<"Case "<<i<<":\n",#endifMain();return 0;
}
