1681 公共祖先

原创 2017年10月25日 18:00:21

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;
}

二叉树最近公共祖先问题(O(n) time 且只遍历一遍,O(1) Space (不考虑函数调用栈的空间))

Tarjan算法很精妙,但是使用了并查集,需要额外O(n)的存储空间。上面博客中给的第三个方法也是需要记录根到节点的路径,需要O(log n)空间,当然考虑到一般情况下我们遍历树都是递归的方式,所以本...
  • smartxxyx
  • smartxxyx
  • 2015年04月02日 11:00
  • 5400

二叉树系列——两个节点的最低公共祖先

出处:https://segmentfault.com/a/1190000003509399 二叉搜索树: Given a binary search tree (BST), find the ...
  • liuyi1207164339
  • liuyi1207164339
  • 2016年03月17日 14:31
  • 1188

剑指offer 面试题50—树中两个节点的最低公共祖先

#include #include #include using namespace std; typedef struct TreeNode { int data; ...
  • wtyvhreal
  • wtyvhreal
  • 2015年05月07日 19:23
  • 1811

51Nod 1681 公共祖先 [树状数组做法]

树状数组+dfs序+思路
  • SenyeLicone
  • SenyeLicone
  • 2017年05月26日 09:40
  • 259

51nod1681公共祖先(dfs序+树状数组)

1681 公共祖先
  • qq_38601996
  • qq_38601996
  • 2017年10月25日 18:54
  • 41

51nod 1681 公共祖先【树状数组】【DFS序】

Description有一个庞大的家族,共n人。已知这n个人的祖辈关系正好形成树形结构(即父亲向儿子连边)。在另一个未知的平行宇宙,这n人的祖辈关系仍然是树形结构,但他们相互之间的关系却完全不同了,原...
  • FYOIER
  • FYOIER
  • 2017年10月30日 20:06
  • 70

【DFS序+树状数组】51Nod 1681 公共祖先

题面在这里稍稍转化一下题面就可以得到,其实是求 ∑x=1nC2s \sum_{x=1}^n C_s^2 其中s是在两棵树中都是x的后代的节点个数如何统计s呢?其实也很简单先对一棵树处理出DFS序...
  • linkfqy
  • linkfqy
  • 2017年10月30日 19:52
  • 475

51Nod 1681 公共祖先 [主席树做法]

dfs序+主席树+思路
  • SenyeLicone
  • SenyeLicone
  • 2017年05月26日 09:12
  • 239

51nod 1681 公共祖先 主席树dfs序

有一个庞大的家族,共n人。已知这n个人的祖辈关系正好形成树形结构(即父亲向儿子连边)。 在另一个未知的平行宇宙,这n人的祖辈关系仍然是树形结构,但他们相互之间的关系却完全不同了,原来的祖先可能变成了...
  • qq_35866453
  • qq_35866453
  • 2017年02月27日 22:27
  • 92

51nod 1681公共祖先

1681 公共祖先 有一个庞大的家族,共n人。已知这n个人的祖辈关系正好形成树形结构(即父亲向儿子连边)。 在另一个未知的平行宇宙,这n人的祖辈关系仍然是树形结构,但他们相互之间的关系却完全不同了...
  • Jokercold
  • Jokercold
  • 2017年10月20日 19:25
  • 109
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:1681 公共祖先
举报原因:
原因补充:

(最多只允许输入30个字)