毛毛虫

[题目链接](https://www.nowcoder.com/practice/02b12ffe5e374b85bfa7e708abfb038a)

思路

在以 1 为根的有根树上,两只毛毛虫分别从节点 出发,只能往更深的方向走,走到叶子节点停下。问所有可能到达的 二元组个数。

关键观察

毛毛虫从节点 出发,能到达的叶子节点恰好就是 的子树中所有的叶子。因此每次询问的答案就是:

$$

其中 表示节点 的子树中叶子节点的数量。如果 本身就是叶子(无子节点),则

预处理

由于输入已经按编号顺序给出了每个节点的父亲(),且保证 ,所以我们可以直接按编号从大到小累加:

  • 初始化所有叶子节点的 (叶子就是没有子节点的节点)。
  • 从编号 ,将 累加到 上。

这样无需建图,也无需 DFS, 即可完成预处理。

回答询问

读入所有 ,对每个询问计算 ,全部异或起来输出即可。

样例验证

树结构:1 的子节点为 2、3;2 的子节点为 4、5;3 的子节点为 6、7、8。

  • :节点 4-8 各为 1,节点 2 为 2,节点 3 为 3,节点 1 为 5。
  • 查询 (4,5):;(2,3):;(1,2):;(5,8):
  • ,与样例输出一致。

复杂度

  • 时间复杂度,预处理 ,回答询问
  • 空间复杂度

代码

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

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);

    int n, Q;
    cin >> n >> Q;

    vector<int> fa(n + 1);
    vector<long long> leaf(n + 1, 0);
    vector<bool> hasChild(n + 1, false);

    for(int i = 2; i <= n; i++){
        cin >> fa[i];
        hasChild[fa[i]] = true;
    }

    // 叶子节点 leafCnt = 1,然后从大到小累加到父节点
    for(int i = 1; i <= n; i++){
        if(!hasChild[i]) leaf[i] = 1;
    }
    for(int i = n; i >= 2; i--){
        leaf[fa[i]] += leaf[i];
    }

    vector<int> a(Q), b(Q);
    for(int i = 0; i < Q; i++) cin >> a[i];
    for(int i = 0; i < Q; i++) cin >> b[i];

    long long ans = 0;
    for(int i = 0; i < Q; i++){
        ans ^= leaf[a[i]] * leaf[b[i]];
    }
    cout << ans << "\n";
    return 0;
}
import java.util.*;
import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StreamTokenizer st = new StreamTokenizer(br);

        st.nextToken(); int n = (int) st.nval;
        st.nextToken(); int Q = (int) st.nval;

        int[] fa = new int[n + 1];
        long[] leaf = new long[n + 1];
        boolean[] hasChild = new boolean[n + 1];

        for (int i = 2; i <= n; i++) {
            st.nextToken();
            fa[i] = (int) st.nval;
            hasChild[fa[i]] = true;
        }

        for (int i = 1; i <= n; i++) {
            if (!hasChild[i]) leaf[i] = 1;
        }
        for (int i = n; i >= 2; i--) {
            leaf[fa[i]] += leaf[i];
        }

        int[] a = new int[Q], b = new int[Q];
        for (int i = 0; i < Q; i++) {
            st.nextToken();
            a[i] = (int) st.nval;
        }
        for (int i = 0; i < Q; i++) {
            st.nextToken();
            b[i] = (int) st.nval;
        }

        long ans = 0;
        for (int i = 0; i < Q; i++) {
            ans ^= leaf[a[i]] * leaf[b[i]];
        }
        System.out.println(ans);
    }
}