有一个庞大的家族,共n人。已知这n个人的祖辈关系正好形成树形结构(即父亲向儿子连边)。
在另一个未知的平行宇宙,这n人的祖辈关系仍然是树形结构,但他们相互之间的关系却完全不同了,原来的祖先可能变成了后代,后代变成的同辈……
两个人的亲密度定义为在这两个平行宇宙有多少人一直是他们的公共祖先。
整个家族的亲密度定义为任意两个人亲密度的总和。
Input
第一行一个数n(1<=n<=100000)
接下来n-1行每行两个数x,y表示在第一个平行宇宙x是y的父亲。
接下来n-1行每行两个数x,y表示在第二个平行宇宙x是y的父亲。
Output
一个数,表示整个家族的亲密度。
Input示例
5
1 3
3 5
5 4
4 2
1 2
1 3
3 4
1 5
Output示例
6
思路:
利用dfs统计第一个宇宙中的父子关系的起点和终点,al[i]里记录节点i在第一次dfs中的位置。在第二个宇宙中继续利用dfs,根据新的父子关系添加节点。最后利用排列组合从n个节点任意取出两个来计算。统计和时利用树状数组。
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 1;
int n, x, y, fi[MAXN], w[2 * MAXN], ne[2 * MAXN], cnt, al[MAXN], ar[MAXN], root;
ll ans, c[MAXN];
bool isChild[MAXN];
void add(int u, int v)
{
w[++cnt] = v;
ne[cnt] = fi[u];
fi[u] = cnt;
w[++cnt] = u;
ne[cnt] = fi[v];
fi[v] = cnt;
}
int lowbit(int x)
{
return x & (-x);
}
void add1(int x)
{
while (x <= n + 1)
{
c[x]++;
x += lowbit(x);
}
}
int getSum(int x)
{
ll result = 0;
while (x > 0)
{
result += c[x];
x -= lowbit(x);
}
return result;
}
void dfs1(int u, int fa)
{
al[u] = ++cnt;
for (int i = fi[u]; i; i = ne[i])
{
if (w[i] != fa)
{
dfs1(w[i], u);
}
}
ar[u] = cnt;
}
void dfs2(int u, int fa)
{
ll now = getSum(ar[u]) - getSum(al[u]-1);
add1(al[u]);
for (int i = fi[u]; i; i = ne[i])
{
if (w[i] != fa)
{
dfs2(w[i], u);
}
}
now = getSum(ar[u]) - getSum(al[u] - 1) - now - 1;
ans += now * (now - 1) / 2;
}
int main()
{
cin >> n;
memset(isChild, false, sizeof(isChild));
for (int i = 1; i < n; i++)
{
cin >> x >> y;
isChild[y] = true;
add(x, y);
}
for (root = 1; isChild[root]; root++);
cnt = 0;
dfs1(root, 0);
memset(fi, 0, sizeof(fi));
memset(isChild, false, sizeof(isChild));
cnt = 0;
for (int i = 1; i < n; i++)
{
cin >> x >> y;
isChild[y] = true;
add(x, y);
}
for (root = 1; isChild[root]; root++);
cnt = 0;
dfs2(root, 0);
cout << ans << endl;
return 0;
}