E1. PermuTree (easy version)

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 a of length n, let f(a) be the number of pairs of vertices (u,v) such that au<alca(u,v)<av. Here, 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.

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 (2n5000).

The second line contains n1 integers p2,p3,,pn (1pi<i) indicating that there is an edge between vertices i and pi.

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 lca(2,3)=1 and 1<2<4,
(2,4), since lca(2,4)=1 and 1<2<5,
(2,5), since lca(2,5)=1 and 1<2<3,
(5,4), since lca(5,4)=3 and 3<4<5.

The tree in the third test:

The tree in the fourth test:

 

解题思路

  对于所有合法的数对(u,v),按照其最近公共祖先lca(u,v)进分类可得到n类。将最近公共祖先固定,分别求当lca(u,v)=w时,满足au<aw<av的数对的最大数目。问题等价于考虑以w为根节点的子树,并且uv分别在以w所有儿子所构成的不同子树中,满足合法数对的最大数量(当w没有儿子或只有一个儿子,那么不存在合法的数对,因为不等式不成立,或者是最近公共祖先不是w)。

  因此在固定了最近公共祖先为w后,假设节点wm个儿子,那么我们只用关心在以其m个儿子所构成的各个子树中,有多少个节点满足au<aw(那么剩下的节点自然满足大于aw)。假设以第i个儿子为根的子树大小是si,并且有bi(0bisi)个节点满足au<aw,那么所有以w为最近公共祖先的合法数对的数量就是(s1b1)(0+b2++bm)+(s2b2)(b1+0++bm)++(smbm)(b1+b2++0)

  事实上对于任意的满足0bisibi,都可以找到与之对应的排列a。先定义集合Lx表示往以x为根的子树中所有节点所表示的位置填入的数,集合的大小就是子树的大小。mi表示节点i的儿子数量。构造的方法如下:

  1. 先从根节点x=1开始,此时Lx={1,2,,n}
  2. 假设根节点为xx的儿子分别为y1,,ymxb1,,bmx对应的最优值是c1,,cmxLy1,,Lym均为空集。
  3. 对于从1mx的每一个i,依次从Lx中选出前ci个最小的数放到Lyi中,并从Lx中删除。
  4. Lx中选出最小的数作为ax,并从Lx中删除。
  5. 对于从1mx的每一个i,依次从Lx中选出前sici个最小的数放到Lyi中,并从Lx中删除。
  6. 对于每一个儿子y1,,ymx,重复上述过程,直到不存在儿子。

  我们现在要做的是最大化上述表达式的值。定义状态f(i,j)表示固定最近公共祖先w(以w为根的子树)后,考虑uv在前i个儿子所构成的子树中,并且k=1ibk=j,所合法数对的最大值。根据第i个子树中有多少个节点满足au<aw来进行状态划分,定义Si=k=1isk,状态转移方程为f(i,j)=maxmax{0,jSi1 }bimin{si,j}{f(i1,jbi)+bi(Si1(jbi))+(sibi)(jbi)}

  其中限制max{0,jSi1}bimin{si,j}是根据不等式组{0bisi0jbiSi1得到。f(i1,jbi)则表示数对(u,v)均在前i1个子树时所得到的最大数量。bi(Si1(jbi))表示u在第i个子树,v在前i1个子树的数对数量。(sibi)(jbi)表示u在前i1个子树,v在第i个子树的数对数量。

  因此最近公共祖先为w的合法数对的最大数量就是max0jSm{f(m,j)}。又因为以不同节点为最近公共祖先的合法数对的最大值是相互独立的,因此最终答案就是将分别求到的最大值累加,即ans=i=1nmax0jSmi{f(mi,j)}

  看上去总的时间复杂度好像是O(n3),实际上应该是O(n2),这里先贴代码,下面再补充证明。参考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 }

  可以知道计算量大约为mSm+s2S1+s3S2++smSm1

  其中mSm表示总循环次数。s2S1+s3S2++smSm1表示最里面那层循环的代码的总计算量。考虑每一个节点都作为最近公共祖先的情况,那么整一个dp的计算量大概就是    x=1n(mxSmx+sx,2Sx,1+sx,3Sx,2++sx,mSx,m1)n2+x=1n(sx,2Sx,1+sx,3Sx,2++sx,mSx,m1)n2+n(n1)2

  这里简单证明一下x=1n(sx,2Sx,1+sx,3Sx,2++sx,mSx,m1)n(n1)2。通过几何意义进行解释,现在有一个由n个节点,没有任何边的无向图。可以把sx,jSx,j1理解成在以x构成的子树中,其中以第j个儿子为根的子树中的每一个点与前j1个儿子为根的每一个子树中的所有节点连一条边,这样就会有sx,jSx,j1条边。每一条边只会出现一次(因为每次都在子树内部连边),而一个由n个节点构成的无向图最多有Cn2=n(n1)2条边,因此上界就是n(n1)2

  因此时间复杂度为O(n2)

 

参考资料

  Codeforces Round #890 (Div. 2) Editorial:https://codeforces.com/blog/entry/119058

posted @   onlyblues  阅读(67)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
Web Analytics
点击右上角即可分享
微信分享提示