*题解:P14721 [RMI 2025] 橙子 / Oranges
解析
首先需要观察到一只海豚能控制两个点。于是考虑 DP,设 \(f_{i,j}\) 表示在以 \(i\) 为根的子树里放了 \(j\) 只海豚,使得子树安全的方案数。但是我们发现这样是不行的,因为子树的根可能由根的父亲控制,所以要多加一维 \(0/1\) 表示根以外的结点被控制且根没有 / 有被控制。这样还没完,根还可能被儿子所控制,所以再加一维 \(0/1\) 表示根上没有 / 有放海豚。
然而还是不行,因为我们不知道有海豚的儿子其海豚是拿来控制儿子还是拿来控制父亲。故再次更改状态为 \(f_{i,j,0/1/2/3}\) 表示以 \(i\) 为根的子树内放了 \(j\) 只海豚,且根以外的结点都被控制且根没有被控制 / 根上有海豚且海豚可控制父亲 / 根上有海豚且海豚控制儿子 / 根被控制但根上没有海豚的方案数。
转移类似树形背包,转移方程见代码。因为不知道怎么表述。
时间复杂度 \(O(N ^ 2)\)。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 6000 + 5,mod = 998244353;
typedef long long ll;
int f[N][N][4],g[N][4];
int siz[N];
vector<int> t[N];
void dfs(int x,int fa){
siz[x] = 1;
for(int nx : t[x])if(nx != fa){
dfs(nx,x);
}
g[0][0] = 1;
g[1][1] = 1;
for(int nx : t[x])if(nx != fa){
for(int i=siz[x];i>=0;i--){
for(int j=0;j<=siz[nx];j++){
//没有海豚但被控制,儿子至少有一个贡献父亲
f[x][i + j][3] = (f[x][i + j][3] + 1ll * g[i][0] * f[nx][j][1] + 1ll * g[i][3] * (1ll * f[nx][j][1] + f[nx][j][2] + f[nx][j][3])) % mod;
//根有海豚,贡献儿子,儿子有且仅有一个不被控制
f[x][i + j][2] = (f[x][i + j][2] + 1ll * g[i][2] * (1ll * f[nx][j][1] + f[nx][j][2] + f[nx][j][3])) % mod;
if(i){
f[x][i + j][2] = (f[x][i + j][2] + 1ll * (g[i - 1][0] + g[i - 1][3]) * f[nx][j][0]) % mod;
}
//根有海豚,贡献父亲。儿子均被控制
f[x][i + j][1] = (f[x][i + j][1] + 1ll * g[i][1] * (1ll * f[nx][j][1] + f[nx][j][2] + f[nx][j][3])) % mod;
//根不被控制,儿子均不贡献父亲,均被控制
f[x][i + j][0] = (f[x][i + j][0] + 1ll * g[i][0] * (f[nx][j][2] + f[nx][j][3])) % mod;
}
}
siz[x] += siz[nx];
for(int i=0;i<=siz[x];i++){
for(int j=0;j<=3;j++){
g[i][j] = f[x][i][j];
f[x][i][j] = 0;
}
}
}
for(int i=0;i<=siz[x];i++){
for(int j=0;j<=3;j++){
f[x][i][j] = g[i][j];
g[i][j] = 0;
}
}
}
vector<int> solve(int n,vector<int> u,vector<int> v){
for(int i=0;i<n - 1;i++){
t[u[i]].push_back(v[i]);
t[v[i]].push_back(u[i]);
}
dfs(0,-1);
vector<int> res(n + 1);
for(int i=0;i<=n;i++){
res[i] = (1ll * f[0][i][1] + f[0][i][2] + f[0][i][3]) % mod;
}
return res;
}

浙公网安备 33010602011771号