CF1292 C. Xenon's Attack on the Gangs

$n$个结点的树,给每条边分配$[0,n-2]$的权值。$n\leq 3000$

求$MAX(\sum_{i<j} {mex(i,j)})$。

$mex$指最小的未出现的非负整数。如$mex(1,0,3,4)=2$

考虑如何计算$mex$,如果某个权值$x$的路径独立与$x-1$,$mex\leq x-1$,捆绑在一起就是$mex\leq x-2, mex >x$。

考虑吧$[0,x]$捆绑在一起$mex\geq x+1$

贪心的去想把所有数字放在一条链上,这样尽可能都满足$mex\geq x+1$
考虑一条链

考虑这是一棵树,可以处理每个结点$i$相对与$j$的子树大小和父亲。
树上记忆化$dp$每一条链即可。

代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <cstring>
#include <vector>
#include <map>
using namespace std;
typedef long long ll;
const int N = 3e3 + 10;
const int mod = 1e9 + 7;
int siz[N][N];
int fa[N][N];
vector<int> g[N];
ll dp[N][N];
void pre(int x, int f, int rt)
{
siz[rt][x] = 1;
for (int to : g[x])
{
if (to == f)
continue;
pre(to, x, rt);
siz[rt][x] += siz[rt][to];
fa[rt][to] = x;
}
}
ll f(int x, int y)
{
if (dp[x][y])
return dp[x][y];
if (x == y)
return 0;
return dp[x][y] = siz[x][y] * siz[y][x] + max(f(x, fa[x][y]), f(fa[y][x], y));
}
int n;
int main()
{
scanf("%d", &n);
for (int i = 1; i < n; i++)
{
int u, v;
scanf("%d%d", &u, &v);
g[u].push_back(v);
g[v].push_back(u);
}
for (int i = 1; i <= n; i++)
pre(i, 0, i);
ll ans = 0;
for (int i = 1; i <= n; i++)
for (int j = 1; j <= n; j++)
ans = max(ans, f(i, j));
printf("%lld\n", ans);
}