毛毛虫 联想24年春招-后端开发工程师

毛毛虫 联想24年春招-后端开发工程师
树是一张 n 个点 n-1 条边的无向联通图,每两个点都有唯一的一条简单路径。有根树是指以其中一个点为根节点的树,叶子节点是指除根节点外度数为 1 的节点。一个点的度数是指与其相连的点的个数。有根树上,一个点的深度是指其与根节点之间的简单路径的边数。
在某一棵以 1 为根的有根树上,有两个节点 a,b 上各存在一只毛毛虫。这两只毛毛虫只会往深度更大的点前进,当毛毛虫走到叶子节点时会停下。设第一只毛毛虫可能走到的节点为p1,第二只毛毛虫可能走到的节点为p2,你想要知道二元组(p1,p2)的个数(p1可以等于p2)。一共有 Q 次询问。

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 256M,其他语言512M
输入描述:
第一行两个正整数n,Q(1 <= n,Q <= 50000)。
第二行 n-1 个正整数f2,f3…fn,(1 <= fi < i),表示树上节点 i 与 fi 之间有一条边。
第三行 Q个正整数a1,a2…aQ(1<= ai <=n) ;
第四行 Q个正整数b1,b2…bQ(1<=bi<=n, ai != bi)。
第三行和第四行表示 ai,bi 是第i 个查询对应的两只毛毛虫所在的节点。

输出描述:
为了避免输出量较大,你需要输出所有询问的答案的异或和。

此题描述较为复杂,但属于经典的树形dp的题目,难度中等偏上。主要注意一下几点:

  1. 构建树:题目描述的构建树的方式其实不是很严谨,但是推测是边,指的就是父亲节点。
  2. 此题是多叉树,而不是普通的二叉树。
  3. 由于多次查询,由递归记忆化的思想,可以使用数组存储每个节点的叶子孩子数量。
#include <iostream>
#include <vector>
using namespace std;

struct Node{
    vector<Node*> sons;
    int val=0;
    Node(int value){
        val=value;
    }
};

int getLeafChildNum(vector<Node*>& arr, int p,vector<int> & leafChildNum){
    if(leafChildNum[p]!=-1){
        return leafChildNum[p];
    }
    if(arr[p]->sons.size()==0){
        leafChildNum[p]=1;
        return 1;
    }
    leafChildNum[p]=0;
    for(int i=0;i<arr[p]->sons.size();++i){
    	//向子节点要答案:dp的思想,解决小问题,再利用其答案。
        leafChildNum[p]+=getLeafChildNum(arr, arr[p]->sons[i]->val, leafChildNum);
    }
    return leafChildNum[p];
}

int zhongshu(vector<Node*>& arr, int p1,int p2,vector<int> & leafChildNum){
    return getLeafChildNum(arr,p1,leafChildNum) * getLeafChildNum(arr,p2,leafChildNum);
}

int main() {
    int n, Q;
    cin>>n>>Q;
    vector<Node *> arr(n+1);
    vector<int> leafChildNum(n+1);
    for (int i=0; i<=n; ++i) {
        arr[i]=nullptr;
        leafChildNum[i]=-1;
    }
    Node* root = new Node(1); //边可能是根,可能是儿子,是否按照顺序
    arr[1]=root;
    for(int i=2;i<=n;++i){
        Node* cur=new Node(i);
        int parval;
        cin>>parval;
        Node* par=arr[parval];
        par->sons.push_back(cur);
        arr[i]=cur;
    }
    vector<int> p1(Q),p2(Q);
    for(int i=0;i<Q;++i){
        cin>>p1[i];
    }
    for(int i=0;i<Q;++i){
        cin>>p2[i];
    }

    int cnt=0;
    for(int i=0;i<Q;++i){
        int res = zhongshu(arr,p1[i],p2[i],leafChildNum);
        // cout<<res<<endl;
        cnt^=res;
    }
    cout<<cnt;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值