CodeForces 1856E1 PermuTree (easy version)

发布时间 2023-08-06 08:33:11作者: zltzlt

洛谷传送门

CF 传送门

考虑局部贪心,假设我们现在在 \(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;
}