E1. PermuTree (easy version)
This is the easy version of the problem. The differences between the two versions are the constraint on $n$ and the time limit. You can make hacks only if both versions of the problem are solved.
You are given a tree with $n$ vertices rooted at vertex $1$.
For some permutation$^\dagger$ $a$ of length $n$, let $f(a)$ be the number of pairs of vertices $(u, v)$ such that $a_u < a_{\operatorname{lca}(u, v)} < a_v$. Here, $\operatorname{lca}(u,v)$ denotes the lowest common ancestor of vertices $u$ and $v$.
Find the maximum possible value of $f(a)$ over all permutations $a$ of length $n$.
$^\dagger$ A permutation of length $n$ is an array consisting of $n$ distinct integers from $1$ to $n$ in arbitrary order. For example, $[2,3,1,5,4]$ is a permutation, but $[1,2,2]$ is not a permutation ($2$ appears twice in the array), and $[1,3,4]$ is also not a permutation ($n=3$ but there is $4$ in the array).
Input
The first line contains a single integer $n$ ($2 \le n \le 5000$).
The second line contains $n - 1$ integers $p_2,p_3,\ldots,p_n$ ($1 \le p_i < i$) indicating that there is an edge between vertices $i$ and $p_i$.
Output
Output the maximum value of $f(a)$.
Examples
input
5 1 1 3 3
output
4
input
2 1
output
0
input
6 1 2 2 1 5
output
7
input
4 1 1 1
output
2
Note
The tree in the first test:
One possible optimal permutation $a$ is $[2, 1, 4, 5, 3]$ with $4$ suitable pairs of vertices:
$(2, 3)$, since $\operatorname{lca}(2, 3) = 1$ and $1 < 2 < 4$,
$(2, 4)$, since $\operatorname{lca}(2, 4) = 1$ and $1 < 2 < 5$,
$(2, 5)$, since $\operatorname{lca}(2, 5) = 1$ and $1 < 2 < 3$,
$(5, 4)$, since $\operatorname{lca}(5, 4) = 3$ and $3 < 4 < 5$.
The tree in the third test:
The tree in the fourth test:
解题思路
对于所有合法的数对$(u, v)$,按照其最近公共祖先$\operatorname{lca}(u,v)$进分类可得到$n$类。将最近公共祖先固定,分别求当$\operatorname{lca}(u,v) = w$时,满足$a_u < a_w < a_v$的数对的最大数目。问题等价于考虑以$w$为根节点的子树,并且$u$和$v$分别在以$w$所有儿子所构成的不同子树中,满足合法数对的最大数量(当$w$没有儿子或只有一个儿子,那么不存在合法的数对,因为不等式不成立,或者是最近公共祖先不是$w$)。
因此在固定了最近公共祖先为$w$后,假设节点$w$有$m$个儿子,那么我们只用关心在以其$m$个儿子所构成的各个子树中,有多少个节点满足$a_u < a_w$(那么剩下的节点自然满足大于$a_w$)。假设以第$i$个儿子为根的子树大小是$s_i$,并且有$b_i$$(0 \leq b_i \leq s_i)$个节点满足$a_u < a_w$,那么所有以$w$为最近公共祖先的合法数对的数量就是$$(s_1 - b_1) \cdot (0 + b_2 + \ldots + b_m) + (s_2 - b_2) \cdot (b_1 + 0 + \ldots + b_m) + \ldots + (s_m - b_m) \cdot (b_1 + b_2 + \ldots + 0)$$
事实上对于任意的满足$0 \leq b_i \leq s_i$的$b_i$,都可以找到与之对应的排列$a$。先定义集合$L_x$表示往以$x$为根的子树中所有节点所表示的位置填入的数,集合的大小就是子树的大小。$m_i$表示节点$i$的儿子数量。构造的方法如下:
- 先从根节点$x=1$开始,此时$L_x = \{ 1, 2, \ldots, n \}$。
- 假设根节点为$x$,$x$的儿子分别为$y_1, \ldots, y_{m_x}$,$b_1, \ldots, b_{m_x}$对应的最优值是$c_1, \ldots, c_{m_x}$。$L_{y_1}, \ldots, L_{y_m}$均为空集。
- 对于从$1$到$m_x$的每一个$i$,依次从$L_x$中选出前$c_i$个最小的数放到$L_{y_i}$中,并从$L_x$中删除。
- 从$L_x$中选出最小的数作为$a_x$,并从$L_x$中删除。
- 对于从$1$到$m_x$的每一个$i$,依次从$L_x$中选出前$s_i - c_i$个最小的数放到$L_{y_i}$中,并从$L_x$中删除。
- 对于每一个儿子$y_1, \ldots, y_{m_x}$,重复上述过程,直到不存在儿子。
我们现在要做的是最大化上述表达式的值。定义状态$f(i,j)$表示固定最近公共祖先$w$(以$w$为根的子树)后,考虑$u$和$v$在前$i$个儿子所构成的子树中,并且$\sum\limits_{k=1}^{i}{b_k} = j$,所合法数对的最大值。根据第$i$个子树中有多少个节点满足$a_u < a_w$来进行状态划分,定义$S_i = \sum\limits_{k=1}^{i}{s_k}$,状态转移方程为$$f(i,j) = \max\limits_{\max\{0, j - S_{i-1} \ \} \leq b_i \leq \min\{s_i, j\}} \left\{ f(i-1, j-b_i) + b_i \cdot \left( S_{i-1} - (j - b_i) \right) + (s_i - b_i) \cdot (j - b_i) \right\}$$
其中限制$\max\{0, j - S_{i-1}\} \leq b_i \leq \min\{s_i, j\}$是根据不等式组$\begin{cases} 0 \leq b_i \leq s_i \\ 0 \leq j - b_i \leq S_{i - 1} \end{cases}$得到。$f(i-1, j-b_i)$则表示数对$(u,v)$均在前$i-1$个子树时所得到的最大数量。$b_i \cdot \left( S_{i-1} - (j - b_i) \right)$表示$u$在第$i$个子树,$v$在前$i-1$个子树的数对数量。$(s_i - b_i) \cdot (j - b_i)$表示$u$在前$i-1$个子树,$v$在第$i$个子树的数对数量。
因此最近公共祖先为$w$的合法数对的最大数量就是$\max\limits_{0 \leq j \le S_m}{\left\{ f(m,j) \right\}}$。又因为以不同节点为最近公共祖先的合法数对的最大值是相互独立的,因此最终答案就是将分别求到的最大值累加,即$$\text{ans} = \sum\limits_{i=1}^{n}{\max\limits_{0 \leq j \le S_{m_i}}{\left\{ f(m_i,j) \right\}}}$$
看上去总的时间复杂度好像是$O(n^3)$,实际上应该是$O(n^2)$,这里先贴代码,下面再补充证明。参考01背包的空间优化,这里也可以将$f(i,j)$优化为$f(j)$。
AC代码如下:
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int N = 5010; 5 6 int head[N], e[N], ne[N], idx; 7 int sz[N]; 8 int f[N]; 9 10 void add(int v, int w) { 11 e[idx] = w, ne[idx] = head[v], head[v] = idx++; 12 } 13 14 int dfs(int u) { 15 sz[u] = 1; 16 int ret = 0; 17 for (int i = head[u]; i != -1; i = ne[i]) { 18 ret += dfs(e[i]); 19 sz[u] += sz[e[i]]; 20 } 21 memset(f, 0, sizeof(f)); 22 for (int i = head[u], s = 0; i != -1; i = ne[i]) { 23 for (int j = s + sz[e[i]]; j >= 0; j--) { 24 for (int k = max(0, j - s); k <= min(sz[e[i]], j); k++) { 25 f[j] = max(f[j], f[j - k] + k * (s - (j - k)) + (sz[e[i]] - k) * (j - k)); 26 } 27 } 28 s += sz[e[i]]; 29 } 30 return ret + *max_element(f, f + sz[u]); 31 } 32 33 int main() { 34 int n; 35 scanf("%d", &n); 36 memset(head, -1, sizeof(head)); 37 for (int i = 2; i <= n; i++) { 38 int x; 39 scanf("%d", &x); 40 add(x, i); 41 } 42 printf("%d", dfs(1)); 43 44 return 0; 45 }
当最近公共祖先固定后,看状态转移的代码:
1 for (int i = head[u], s = 0; i != -1; i = ne[i]) { 2 for (int j = s + sz[e[i]]; j >= 0; j--) { 3 for (int k = max(0, j - s); k <= min(sz[e[i]], j); k++) { 4 f[j] = max(f[j], f[j - k] + k * (s - (j - k)) + (sz[e[i]] - k) * (j - k)); 5 } 6 } 7 s += sz[e[i]]; 8 }
可以知道计算量大约为$$m \cdot S_m + s_2 \cdot S_1 + s_3 \cdot S_2 + \ldots + s_m \cdot S_{m - 1}$$
其中$m \cdot S_m$表示总循环次数。$s_2 \cdot S_1 + s_3 \cdot S_2 + \ldots + s_m \cdot S_{m - 1}$表示最里面那层循环的代码的总计算量。考虑每一个节点都作为最近公共祖先的情况,那么整一个dp的计算量大概就是$$\begin{align*} & \ \ \ \ \sum\limits_{x=1}^{n}{\left(m_x \cdot S_{m_x} + s_{x,2} \cdot S_{x,1} + s_{x,3} \cdot S_{x,2} + \ldots + s_{x,m} \cdot S_{x,m-1} \right)} \\ &\leq n^2 + \sum\limits_{x=1}^{n}{\left(s_{x,2} \cdot S_{x,1} + s_{x,3} \cdot S_{x,2} + \ldots + s_{x,m} \cdot S_{x,m-1} \right)} \\ &\leq n^2 + \frac{n \cdot (n - 1)}{2} \end{align*}$$
这里简单证明一下$\sum\limits_{x=1}^{n}{\left(s_{x,2} \cdot S_{x,1} + s_{x,3} \cdot S_{x,2} + \ldots + s_{x,m} \cdot S_{x,m-1} \right)} \leq \frac{n \cdot (n - 1)}{2}$。通过几何意义进行解释,现在有一个由$n$个节点,没有任何边的无向图。可以把$s_{x,j} \cdot S_{x,j-1}$理解成在以$x$构成的子树中,其中以第$j$个儿子为根的子树中的每一个点与前$j-1$个儿子为根的每一个子树中的所有节点连一条边,这样就会有$s_{x,j} \cdot S_{x,j-1}$条边。每一条边只会出现一次(因为每次都在子树内部连边),而一个由$n$个节点构成的无向图最多有$C_{n}^{2} = \frac{n \cdot (n - 1)}{2}$条边,因此上界就是$\frac{n \cdot (n - 1)}{2}$。
因此时间复杂度为$O(n^2)$。
参考资料
Codeforces Round #890 (Div. 2) Editorial:https://codeforces.com/blog/entry/119058
标签:limits,cdot,PermuTree,节点,leq,int,version,E1,ldots From: https://www.cnblogs.com/onlyblues/p/17611803.html