题解:P14471 [集训队互测 2025] 火花
深刻。美味题。
分析
因为限制与祖先有关,不难想到在 dfs 序上做。
把回溯点也放进去,得到一个长度为
这样可以定义状态函数
棘手的地方在于,第一步选择的点会影响它所有祖先的物品数量。
对于每个
因为
- 在
in_u 时刻剩余s(in_u) 个。 - 在
out_u 时刻剩余c_u - s(out_u) 个。
为了强制至少取了一个物品,在
这样就可以 DP 了。把状态函数定义成
分情况讨论:
-
1. $p_i$ 不取物品。此时整个子树都不取物品,但是可以在第一步被选择。枚举有 $x$ 个在第一步被选择,记 $g_u(x)$ 表示在 $u$ 子树中前 $x$ 大价值和(点 $p$ 的价值定义为 $val_p = \sum\limits_{q\in P(p,1)}^{}v_p$)。有 $f_{out_{p_i}+1}(j,k+x)\gets f_{i}(j,k)+g_{p_i}(x)$。 2. $p_i$ 取物品。枚举取的物品数量 $x$,满足 $x \in [0,k]$。有 $f_{i+1}(j+x,k)\gets f_{i}(j,k)+x\cdot v_{p_i}$。 -
初始化
对于情况
时间复杂度
代码
const int N = 1e4 + 10, inf = 1e18;
int n, K, t;
int c[N], v[N], val[N];
vector<int> e[N];
vector<vector<int>> f[N << 1];
vector<int> g[N];
int p[N << 1], in[N], ot[N], cnt;
int qu[N], hh, tt;
il vector<int> merge(vector<int> v1, vector<int> v2){
vector<int> v3;
v3.emplace_back(0);
int x = 1, y = 1, z = 0;
while(z < t && (x < v1.size() || y < v2.size())){
if(x == v1.size()) v3.emplace_back(v2[y ++]);
else if(y == v2.size()) v3.emplace_back(v1[x ++]);
else if(v1[x] > v2[y]) v3.emplace_back(v1[x ++]);
else v3.emplace_back(v2[y ++]);
++ z;
}
return v3;
}
il void dfs(int u){
g[u].emplace_back(0);
val[u] += v[u];
if(t) g[u].emplace_back(val[u]);
in[u] = ++cnt, p[cnt] = u;
for(auto v: e[u]){
val[v] = val[u], dfs(v);
g[u] = merge(g[u], g[v]);
}
ot[u] = ++cnt, p[cnt] = u;
return ;
}
il void work(int l, int r, int L, int R, int I, int J, int j){
if(L > R || l > r) return ;
if(l == r){
U(k, max(l, L), R) f[J][j][k] = max(f[J][j][k], f[I][j][k - l] + g[p[I]][l]);
return ;
}
int mid = L + R >> 1, mx = -inf, id = l;
U(i, l, min(mid, r)){
int V = f[I][j][mid - i] + g[p[I]][i];
if(V >= mx) mx = V, id = i;
}
f[J][j][mid] = max(f[J][j][mid], mx);
work(l, id, L, mid - 1, I, J, j);
work(id, r, mid + 1, R, I, J, j);
return ;
}
il void solve(){
n = rd, K = rd, t = rd;
U(i, 1, n) c[i] = rd, v[i] = rd;
U(i, 2, n) e[rd].emplace_back(i);
{
dfs(1);
U(i, 0, cnt + 1){
f[i].resize(K + 2);
U(j, 0, K){
f[i][j].resize(t + 2);
U(k, 0, t) f[i][j][k] = -inf;
}
}
U(u, 1, n){
vector<int> fuc; fuc.swap(g[u]);
int sum = 0;
U(i, 0, fuc.size() - 1){
sum += fuc[i];
g[u].emplace_back(sum);
}
}
f[1][0][0] = 0;
}
U(i, 1, cnt){
int u = p[i];
if(in[u] == i){
U(j, 0, K) work(0, g[u].size() - 1, 0, t, i, ot[u] + 1, j);
U(k, 0, t){
hh = 1, tt = 0;
U(j, 0, K){
while(hh <= tt && (f[i][qu[tt]][k] - qu[tt] * v[u]) < (f[i][j][k] - j * v[u])) --tt;
qu[++ tt] = j;
while(hh <= tt && qu[hh] < max(0ll, j - k)) ++hh;
if(hh <= tt) f[i + 1][j][k] = max(f[i + 1][j][k], f[i][qu[hh]][k] - qu[hh] * v[u] + j * v[u]);
}
}
}
else{
U(dk, 0, 1){
U(k, 0, t - dk){
hh = 1, tt = 0;
U(j, 0, K){
while(hh <= tt && qu[hh] < max(0ll, j - (c[u] - (k + dk)))) ++hh;
if(hh <= tt) f[i + 1][j][k + dk] = max(f[i + 1][j][k + dk], f[i][qu[hh]][k] - qu[hh] * v[u] + j * v[u] + dk * val[u]);
while(hh <= tt && (f[i][qu[tt]][k] - qu[tt] * v[u]) < (f[i][j][k] - j * v[u])) --tt;
qu[++ tt] = j;
}
}
}
}
}
int res = 0;
U(j, 0, K) res = max(res, f[cnt + 1][j][t]);
cout << res << "\n";
return ;
}