CodeForces 1856E1 PermuTree (easy version)
考虑局部贪心,假设我们现在在 \(u\),我们希望 \(u\) 不同子树中的 \((v, w), a_v < a_u < a_w\) 的对数尽量多。
我们实际上只关心子树内 \(a_u\) 的相对大小关系,不关心它们具体是什么。如果 \(u\) 只有两个儿子 \(v, w\),我们可以让 \(v\) 子树内的 \(a\) 全部小于 \(w\) 子树内的 \(a\),这样 \(u\) 作为 \(\text{LCA}\) 的贡献是 \(sz_v \times sz_w\),是最大的。
那么对于 \(u\) 有多个儿子的情况,推广可知相当于把 \(u\) 的儿子分成 \(S, T\) 两个集合,最大化 \(\sum\limits_{v \in S} sz_v \times \sum\limits_{v \in T} sz_v\)。考虑做一个 \(sz_v\) 的 01 背包,若能把 \(sz_v\) 分成大小为 \(x\) 的集合,\(u\) 对答案的贡献是 \(x \times (sz_u - 1 - x)\)。取这个的最大值即可。
01 背包暴力做即可,根据树形背包的那套理论,每个点对只会在 \(\text{LCA}\) 处被统计,所以时间复杂度 \(O(n^2)\),可以通过 E1。
code
// Problem: E1. PermuTree (easy version)
// Contest: Codeforces - Codeforces Round 890 (Div. 2) supported by Constructor Institute
// URL: https://codeforces.com/contest/1856/problem/E1
// Memory Limit: 512 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mkp make_pair
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef double db;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;
const int maxn = 5010;
int n, sz[maxn];
bool f[maxn];
ll ans;
vector<int> G[maxn];
void dfs(int u) {
sz[u] = 1;
vector<int> vc;
for (int v : G[u]) {
dfs(v);
sz[u] += sz[v];
vc.pb(sz[v]);
}
mems(f, 0);
f[0] = 1;
int s = 0;
for (int x : vc) {
s += x;
for (int j = s; j >= x; --j) {
f[j] |= f[j - x];
}
}
ll mx = 0;
for (int i = 0; i <= s; ++i) {
if (f[i]) {
mx = max(mx, 1LL * i * (s - i));
}
}
ans += mx;
}
void solve() {
scanf("%d", &n);
for (int i = 2, p; i <= n; ++i) {
scanf("%d", &p);
G[p].pb(i);
}
dfs(1);
printf("%lld\n", ans);
}
int main() {
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}