E. Happy Life in University

E. Happy Life in University

Egor and his friend Arseniy are finishing school this year and will soon enter university. And since they are very responsible guys, they have started preparing for admission already.

First of all, they decided to take care of where they will live for the long four years of study, and after visiting the university's website, they found out that the university dormitory can be represented as a root tree with $n$ vertices with the root at vertex $1$. In the tree, each vertex represents a recreation with some type of activity $a_i$. The friends need to choose $2$ recreations (not necessarily different) in which they will settle. The guys are convinced that the more the value of the following function $f(u, v) = diff(u, lca(u, v)) \cdot diff(v, lca(u, v))$, the more fun their life will be. Help Egor and Arseniy and find the maximum value of $f(u, v)$ among all pairs of recreations!

$^{\dagger} diff(u, v)$ — the number of different activities listed on the simple path from vertex $u$ to vertex $v$.

$^{\dagger} lca(u, v)$ — a vertex $p$ such that it is at the maximum distance from the root and is a parent of both vertex $u$ and vertex $v$.

Input

Each test consists of several test cases. The first line contains a single integer $t$ ($1 \le t \le 10^5$) — the number of test cases. Then follows the description of the test cases.

The first line of each test case contains a single integer $n$ ($1 \le n \le 3 \cdot 10^{5}$).

The second line of each test case contains ${n - 1}$ integers $p_2, p_3, \ldots,p_n$ ($1 \le p_i \le i - 1$), where $p_i$ — the parent of vertex $i$.

The third line of each test case contains ${n}$ integers $a_1, a_2, \ldots,a_n$ ($1 \le a_i \le n$), where $a_i$ — the number of the activity located at vertex $i$.

It is guaranteed that the sum of $n$ over all test cases does not exceed $3 \cdot 10^5$.

Output

For each test case, output the maximum value of $f(u, v)$ for all pairs of recreations $(u, v)$.

Example

input

4
2
1
1 2
7
1 1 2 2 3 3
6 5 2 3 6 5 6
13
1 1 1 2 2 2 3 3 4 5 6 6
2 2 2 1 4 9 7 2 5 2 1 11 2
12
1 1 1 2 2 3 4 4 7 7 6
11 2 1 11 12 8 5 8 8 5 11 7

output

2
9
9
12

Note

Consider the fourth test case. The tree has the following structure:

All recreations are colored. The same colors mean that the activities in the recreations match. Consider the pair of vertices $(11, 12)$, $lca(11, 12) = 1$. Write down all activities on the path from $11$ to $1$ — $[11, 5, 1, 11]$, among them there are $3$ different activities, so $diff(11, 1) = 3$. Also write down all activities on the path from $12$ to $1$ — $[7, 8, 2, 11]$, among them there are $4$ different activities, so $diff(12, 1) = 4$. We get that $f(11, 12) = diff(12, 1) \cdot diff(11, 1) = 4 \cdot 3 = 12$, which is the answer for this tree. It can be shown that a better answer is impossible to obtain.

 

解题思路

  考虑枚举每个节点分别作为 $\text{lca}$ 时 $f(x,y)$ 能取到的最大值(其实就是把所有路径按最高点进行分类)。当枚举到节点 $u$ 作为 $\text{lca}$,很明显 $x$ 和 $y$ 只能在子树 $u$ 中选择。当 $x$ 与 $y$ 中有一个是 $u$ 时 $\text{lca}(x, y) = u$ 显然满足。否则,假设 $v_1$ 和 $v_2$ 是 $u$ 的两个不同的儿子,此时 $x$ 和 $y$ 分别在子树 $v_1$ 与子树 $v_2$ 中选才会有 $\text{lca}(x, y) = u$。

  考虑子树 $u$ 中的所有节点 $x$,不妨假设此时我们已经求得每个 $x$ 到 $u$ 的路径上的 $diff(x, u)$,很明显我们应该在子树 $v_1, v_2, \ldots, v_m$ 中(假设 $u$ 有 $m$ 个儿子)分别选出最大的 $m$ 个 $diff(x, u)$,再从这 $m$ 个 $diff(x, u)$ 中选出两个最大的 $diff(x, u)$ 和 $diff(y, u)$ 的乘积作为 $\text{lca}(x,y) = u$ 时 $f(x,y)$ 的最大值。

  考虑如何维护 $diff(x, u)$。当枚举到 $u$ 时,先通过 dfs 分别求出子树 $v_i$ 中每个节点 $x$ 到 $v_i$ 的路径上的 $diff(x, v_i)$。再考虑如何将 $diff(x, v_i)$ 更新为 $diff(x, u)$。先不管路径上颜色重复的问题,我们直接先对每个 $diff(x, v_i)$ 加 $1$ 更新成 $diff(x, u) = diff(x, v_i) + 1$,表示从 $x$ 到 $u$ 的路径中不同的颜色数目。由于需要对子树加上某个数,因此我们可以用线段树来维护 dfs 序,进行区间加。

  接着为了保证正确性,我们还需要找到路径中含颜色 $w_u$ 的所有节点 $x$,并让 $diff(x, u)$ 减 $1$。容易发现对于任意一个 $w_x = w_u$ 的节点,由于子树 $x$ 中的节点 $y$ 到 $u$ 的路径都会经过 $x$,因此这些路径都含颜色 $w_u$,所以我们只需将子树 $x$ 中所有节点的 $diff(y, u)$ 减 $1$ 即可。为了避免某些子树被重复减,我们选择的这些 $x$ 要保证不存在某个节点是另外一个节点的子节点。事实上满足该条件的 $x$ 就是从 $u$ 到其子树中每个叶子的路径上,距离 $u$ 最近且颜色为 $w_u$ 的节点。

  实现的话只需为每个节点开一个 std::set<std::array<int, 2>>,二元组记录离该节点最近的各种颜色和对应的节点编号。当枚举到 $u$,我们先 dfs 其每个儿子 $v_i$,回溯到 $u$ 时进行启发式合并,即把 st[vi] 的内容加入到 st[u] 中。对于减操作,迭代 st[u] 中颜色是 $w_u$ 的节点,并在对应的 dfs 序通过线段树进行区间减,然后将二元组删掉。最后把 $(w_u, u)$ 插入到 st[u] 中,枚举每个儿子 $v_i$,通过线段树查询子树 $v_i$ 中所有节点的最大的 $diff(x, u)$。

  线段树实现的操作有区间加,查询区间最大值。维护的是 dfs 序中每个节点的 $diff$ 值。

  AC 代码如下,时间复杂度为 $O(n \log^2{n})$:

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;

const int N = 3e5 + 10;

int w[N];
int h[N], e[N], ne[N], idx;
int tin[N], tout[N], sz;
set<array<int, 2>> st[N];
struct Node {
    int l, r, mx, add;
}tr[N * 4];
LL ans;

void add(int u, int v) {
    e[idx] = v, ne[idx] = h[u], h[u] = idx++;
}

void build(int u, int l, int r) {
    tr[u] = {l, r, 0, 0};
    if (l != r) {
        int mid = l + r >> 1;
        build(u << 1, l, mid);
        build(u << 1 | 1, mid + 1, r);
    }
}

void pushdown(int u) {
    if (tr[u].add) {
        tr[u << 1].mx += tr[u].add;
        tr[u << 1].add += tr[u].add;
        tr[u << 1 | 1].mx += tr[u].add;
        tr[u << 1 | 1].add += tr[u].add;
        tr[u].add = 0;
    }
}

void modify(int u, int l, int r, int c) {
    if (tr[u].l >= l && tr[u].r <= r) {
        tr[u].mx += c;
        tr[u].add += c;
    }
    else {
        pushdown(u);
        int mid = tr[u].l + tr[u].r >> 1;
        if (l <= mid) modify(u << 1, l, r, c);
        if (r >= mid + 1) modify(u << 1 | 1, l, r, c);
        tr[u].mx = max(tr[u << 1].mx, tr[u << 1 | 1].mx);
    }
}

int query(int u, int l, int r) {
    if (tr[u].l >= l && tr[u].r <= r) return tr[u].mx;
    pushdown(u);
    int mid = tr[u].l + tr[u].r >> 1, ret = 0;
    if (l <= mid) ret = query(u << 1, l, r);
    if (r >= mid + 1) ret = max(ret, query(u << 1 | 1, l, r));
    return ret;
}

void dfs(int u) {
    tin[u] = ++sz;
    for (int i = h[u]; i != -1; i = ne[i]) {
        int v = e[i];
        dfs(v);
        if (st[u].size() < st[v].size()) st[u].swap(st[v]);    // 启发式合并,将小集合合并到大集合
        st[u].insert(st[v].begin(), st[v].end());
        st[v].clear();    // 记得及时把被合并的集合清空,防止爆空间
    }
    tout[u] = sz;
    modify(1, tin[u], tout[u], 1);    // 先将整个子树的节点的diff值加1
    auto it = st[u].lower_bound({w[u], -1});    // 找到左边第一个颜色为w[u]的节点
    while (it != st[u].end() && it->at(0) == w[u]) {
        modify(1, tin[it->at(1)], tout[it->at(1)], -1);    // 将子树的节点的diff值减1
        it = st[u].erase(it);    // 从集合中删除这个节点
    }
    st[u].insert({w[u], u});    // 把u加到集合,此时u是最靠近且颜色为w[u]的节点
    int mx1 = 1, mx2 = 1;    // 两个1指默认两个节点都选择u
    for (int i = h[u]; i != -1; i = ne[i]) {
        int t = query(1, tin[e[i]], tout[e[i]]);    // 查询以儿子为根的子树中diff的最大值
        if (t >= mx1) mx2 = mx1, mx1 = t;
        else if (t > mx2) mx2 = t;
    }
    ans = max(ans, 1ll * mx1 * mx2);
}

void solve() {
    int n;
    cin >> n;
    idx = 0;
    memset(h, -1, n + 10 << 2);
    for (int i = 2; i <= n; i++) {
        int x;
        cin >> x;
        add(x, i);
    }
    for (int i = 1; i <= n; i++) {
        cin >> w[i];
    }
    sz = ans = 0;
    st[1].clear();
    build(1, 1, n);
    dfs(1);
    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin >> t;
    while (t--) {
        solve();
    }
    
    return 0;
}

 

参考资料

  Codeforces Good Bye 2023:https://www.cnblogs.com/xzmxzm/p/17937569/CF1916

  Codeforces Good Bye 2023 E 个人题解 - Happy life in university:https://zhuanlan.zhihu.com/p/675479821

  Good Bye 2023 tutorial:https://codeforces.com/blog/entry/124138

posted @ 2024-01-14 00:59  onlyblues  阅读(3)  评论(0编辑  收藏  举报
Web Analytics