1681 公共祖先

1681 公共祖先

Description

有一个庞大的家族,共n人。已知这n个人的祖辈关系正好形成树形结构(即父亲向儿子连边)。
在另一个未知的平行宇宙,这n人的祖辈关系仍然是树形结构,但他们相互之间的关系却完全不同了,原来的祖先可能变成了后代,后代变成的同辈……
两个人的亲密度定义为在这两个平行宇宙有多少人一直是他们的公共祖先。
整个家族的亲密度定义为任意两个人亲密度的总和。

Input

第一行一个数n(1<=n<=100000)
接下来n-1行每行两个数x,y表示在第一个平行宇宙x是y的父亲。
接下来n-1行每行两个数x,y表示在第二个平行宇宙x是y的父亲。

Output

一个数,表示整个家族的亲密度。

Input example

5
1 3
3 5
5 4
4 2
1 2
1 3
3 4
1 5

Output example

6

Solution

很神奇的数据结构题,首先,我们先dfs便历第一棵树,记录每个点的入时间戳和出时间戳(这点和tarjan类似),对于第二棵树,我们也进行一遍dfs,对于每一个节点u,我们先记录在第二棵树上,不包含u这个节点的子树时,有多少节点在u所包含的时间戳里出现过,因为出现过就知道这个点所覆盖的时间戳一定不是以u为祖先了,之后我们来便历第二棵树中u的子树,并在便历完之后重新统计有多少个节点在u所包含的时间戳里出现过,两次答案相减,便是两棵子树中都以u为祖先的节点数,设这个差为sum,则这个节点u对答案的贡献为(sum-1)*(sum-2),对于统计u所包含的时间戳中的结点个数,我们可以用树状数组来维护,代码如下:
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
using namespace std;
vector <int> edge[100010];
vector <int> pic[100010];
long long tree[10000000];
struct node{
    int in,out;
}dfn[100010];
int cnt,In[100010],n;
long long ans;
int lowbit(int x){
    return x&(-x);
}
void dfs1(int u,int fa){
    dfn[u].in=++cnt;
    for (int i=0;i<edge[u].size();i++){
        int v=edge[u][i];
        if (v==fa) continue;
        dfs1(v,u);
    }
    dfn[u].out=cnt;
}
long long query(int x){
    long long ans=0;
    for (int i=x;i;i-=lowbit(i)){
        ans+=tree[i];
    }
    return ans;
}
void add(int x,int val){
    for (int i=x;i<=n;i+=lowbit(i)){
        tree[i]+=val;
    } 
}
void dfs2(int u,int fa){
    long long sum=query(dfn[u].out)-query(dfn[u].in-1);
    add(dfn[u].in,1);
    for (int i=0;i<pic[u].size();i++){
        int v=pic[u][i];
        if (v==fa) continue;
        dfs2(v,u);
    }
    sum=query(dfn[u].out)-query(dfn[u].in-1)-sum-1;
    ans+=(sum-1)*sum/2;
}
int main(){
    scanf("%d",&n);
    for (int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        edge[u].push_back(v);
        In[v]++;
    }
    for (int i=1;i<=n;i++)
        if (!In[i]) dfs1(i,-1);
    memset(In,0,sizeof(In));
    for (int i=1;i<n;i++){
        int u,v;
        scanf("%d%d",&u,&v);
        pic[u].push_back(v);
        In[v]++;
    }
    for (int i=1;i<=n;i++)
        if (!In[i]) dfs2(i,-1);
    printf("%lld\n",ans);
    return 0;
}
1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。、可私 6信博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 、可私信6博主看论文后选择购买源代码。 1、资源项目源码均已通过严格测试验证,保证能够正常运行; 2、项目问题、技术讨论,可以给博主私信或留言,博主看到后会第一时间与您进行沟通; 3、本项目比较适合计算机领域相关的毕业设计课题、课程作业等使用,尤其对于人工智能、计算机科学与技术等相关专业,更为适合; 4、下载使用后,可先查看README.md或论文文件(如有),本项目仅用作交流学习参考,请切勿用于商业用途。 5、资源来自互联网采集,如有侵权,私聊博主删除。 、可私信6博主看论文后选择购买源代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值