题意:给出一棵树,每个节点有权值,重量和颜色,问对于每个子树 \(u\),选出一个包含 \(u\) 的虚树,满足重量之和 \(\le X\),相邻两个点颜色不同且权值和最大。
做法:
首先我们会一个非常平凡的 \(O(nX^2)\) 做法,但是这样怎么找都需要一个没有任何性质的 \((max,+)\) 卷积,显然很完蛋,我们考虑为什么会这么慢,因为我们需要枚举节点重量的一个线性组合,所以导致会多个加入,这样很爆炸。
我们不妨先考虑一个更基础的问题,P2014。这个题的题意就是选节点必须选父亲,那么我们考虑这样一个 dp。\(dp_{i,j}\) 代表子树 \(i\),里面选了 \(j\) 个节点,但是这样不够,没有优化空间,我们考虑在做 dp 的时候将 dp 值直接传给儿子,再对自己进行贡献。但是为了保证父亲也选了,我们强制限定向下传的时候 \(1\rightarrow i\) 路径上的若干个点都必选,前面的贡献算过了,在下传的时候加上儿子的权即可,细节可以看代码。
考虑这样做的正确性,因为如果我们进入一个子树,我们就立刻算掉了贡献保证一定有,而保证了这样之后其实元素间就没有什么其他限制了。
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 305;
int dp[maxn][maxn], val[maxn], n, m, dep[maxn];
vector<int> e[maxn];
void dfs(int u) {for (int i = 0; i < e[u].size(); i++) {int v = e[u][i]; dep[v] = dep[u] + 1;for (int j = dep[u] + 1; j <= m; j++)dp[v][j] = dp[u][j - 1] + val[v];dfs(v);for (int j = dep[u] + 1; j <= m; j++)dp[u][j] = max(dp[u][j], dp[v][j]);}
}
signed main() {cin >> n >> m; m++;for (int x, i = 1; i <= n; i++)cin >> x >> val[i], e[x].push_back(i);dep[0] = 1;dfs(0);cout << dp[0][m] << endl;return 0;
}
然后我们考虑对这个题也施加这个 trick,那么先考虑暴力怎么做,\(dp_{u,0/1,i}\) 代表 \(u\) 子树内最上层的颜色为 \(0/1\),重量为 \(i\),最大的权值之和。如果没有颜色直接秒了,考虑有颜色怎么办。
我们如果直接下传,那么会出现一个问题,可能我们会在子树 \(v\) 中从 \(dp_{u,0}\) 转移来一个 \(1\) 颜色的点,\(w\) 子树中又有一个 \(0\) 颜色的,这样就出问题了。
这里有一步天才的想法,我们直接下传 \(dp_{v,0} = dp_{v,1}=dp_{u,0}\),再下传 \(dp_{v,0} = dp_{v,1}=dp_{u,1}\),然后前者只取 \(dp_{v,0}\),后者只取 \(dp_{v,1}\),分讨一下下面的情况发现确实是可以覆盖所有情况的!
但是问题来了,这样的复杂度是 \(\sum 2^{dep}\) 的,那么既然是深度相关,那么完全可以用重链剖分优化一下,启发式一下,直接继承重儿子然后再对于其他的考虑。
分析复杂度,\(T(n) = T(a_1)+2\sum T(a_i)+O(X)\),那么我们应该让后面的 \(\times 2\) 的部分尽量大,那么就让子树间平均,那么就是 \(T(n) = (2k-1)T(\frac{n}{k})+O(X)\),由主定理得到复杂度为 \(O(n^{\log_{k}2k-1}X)\),\(k=2\) 时取到最大,为 \(O(n^{1.59}X)\)。
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int maxn = 205, inf = 9e18;
int n, x, p[maxn], v[maxn], w[maxn], c[maxn], sz[maxn], son[maxn];
vector<int> e[maxn];
void dfs1(int u, int fa) {sz[u] = 1;for (int i = 0; i < e[u].size(); i++) {int v = e[u][i];dfs1(v, u), sz[u] += sz[v];if(sz[son[u]] < sz[v])son[u] = v;}
}
int ans[maxn];
pair<vector<int>, vector<int> > dfs(int u, vector<int> dp, bool use) {if(use) {for (int i = 0; i < e[u].size(); i++) {int v = e[u][i];if(v == son[u])continue;dfs(v, dp, use);}}vector<int> f[2];if(son[u]) {pair<vector<int>, vector<int> > p = dfs(son[u], dp, use);f[0] = p.first, f[1] = p.second;for (int i = 0; i < e[u].size(); i++) {int v = e[u][i];if(v == son[u])continue;f[0] = dfs(v, f[0], 0).first;f[1] = dfs(v, f[1], 0).second;}}elsef[0] = f[1] = dp;if(use) {for (int i = x; i >= w[u]; i--) ans[u] = max(ans[u], f[c[u] ^ 1][i - w[u]] + v[u]);}for (int i = x; i >= w[u]; i--) f[c[u]][i] = max(f[c[u]][i], f[c[u] ^ 1][i - w[u]] + v[u]);return make_pair(f[0], f[1]);
}
signed main() {cin >> n >> x;for (int i = 2; i <= n; i++)cin >> p[i], e[p[i]].push_back(i);dfs1(1, 0);for (int i = 1; i <= n; i++)cin >> v[i] >> w[i] >> c[i];vector<int> v; v.resize(x + 1);for (int i = 1; i <= x; i++)v[i] = -inf;dfs(1, v, 1);for (int i = 1; i <= n; i++)cout << ans[i] << endl;return 0;
}