[传送门]

很明显,可以转化成求每个点在两棵树中对应的子树中有多少个相同的节点,对答案的贡献就是$C(x, 2)$。关键就是怎么求这个东西。
一是,对第一棵树求出dfs序,然后dfs第二棵树,用树状数组维护节点是否遍历到。对应下标就是第一棵树的dfs序,求每个节点递归其子树前后对应子树的区间和,作个差就是对在两棵树同时出现的节点数了。

#include <bits/stdc++.h>
#define ll long long
using namespace std; const int N = 1e5 + ;
int n, degree[N], dfn[N], last[N], tol;
vector<int> G[N];
ll ans; struct BIT {
int tree[N];
inline int lowbit(int x) {
return x & -x;
}
inline void add(int x) {
for (int i = x; i <= n; i += lowbit(i))
tree[i]++;
}
inline int query(int x) {
int ans = ;
for (int i = x; i; i -= lowbit(i))
ans += tree[i];
return ans;
}
} bit; void dfs1(int u) {
dfn[u] = ++tol;
for (auto v: G[u]) dfs1(v);
last[u] = tol;
} void dfs2(int u) {
bit.add(dfn[u]);
int now = bit.query(last[u]) - bit.query(dfn[u] - );
for (auto v: G[u]) dfs2(v);
now = bit.query(last[u]) - bit.query(dfn[u] - ) - now;
ans += 1LL * now * (now - ) / ;
} int main() {
scanf("%d", &n);
for (int i = ; i < n; i++) {
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
degree[v]++;
}
for (int i = ; i <= n; i++)
if (!degree[i])
dfs1(i);
for (int i = ; i <= n; i++) {
G[i].clear();
degree[i] = ;
}
for (int i = ; i < n; i++) {
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
degree[v]++;
}
for (int i = ; i <= n; i++)
if (!degree[i])
dfs2(i);
printf("%lld\n", ans);
return ;
}

第二种做法是主席树,把两棵树的dfs序都求出来,一个节点$u$在第一棵树的子树的区间为$\left[x, y\right]$,在第二棵树的子树的区间为$\left[l, r\right]$,那么就相当于求$\left[l, r\right]$之间有多少个数在$\left[x,y\right]$中。然后主席树维护就行了。那部分感觉有点绕。主席树维护第二颗树dfs序每个位置对应的节点在第一棵树里面的dfs序,然后查询就是查第二棵树dfs序对应的区间中第一棵树的dfs序的区间,相当于就是把$A$用$B$的顺序插入主席树,查询就是查$A$,啊说不清楚,看代码吧。

#include <bits/stdc++.h>
#define ll long long
using namespace std; const int N = 1e5 + ; struct Seg {
struct Tree {
int lp, rp, sum;
} tree[N * ];
int tol;
void update(int &p, int q, int l, int r, int pos) {
tree[p = ++tol] = tree[q];
tree[p].sum++;
if (l == r) return;
int mid = l + r >> ;
if (pos <= mid) update(tree[p].lp, tree[q].lp, l, mid, pos);
else update(tree[p].rp, tree[q].rp, mid + , r, pos);
}
int query(int p, int q, int l, int r, int x, int y) {
if (x <= l && y >= r) return tree[p].sum - tree[q].sum;
int mid = l + r >> ;
int ans = ;
if (x <= mid) ans += query(tree[p].lp, tree[q].lp, l, mid, x, y);
if (y > mid) ans += query(tree[p].rp, tree[q].rp, mid + , r, x, y);
return ans;
}
} seg; int n, degree[N], dfn1[N], last1[N], tol, dfn2[N], last2[N], id[N];
vector<int> G[N];
ll ans;
int root[N]; void dfs(int u, int dfn[], int last[]) {
dfn[u] = ++tol;
for (auto v: G[u]) dfs(v, dfn, last);
last[u] = tol;
} void build() {
for (int i = ; i <= n; i++) id[dfn2[i]] = i;
for (int i = ; i <= n; i++)
seg.update(root[i], root[i - ], , n, dfn1[id[i]]);
} int main() {
scanf("%d", &n);
for (int i = ; i < n; i++) {
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
degree[v]++;
}
for (int i = ; i <= n; i++)
if (!degree[i])
dfs(i, dfn1, last1);
for (int i = ; i <= n; i++) {
G[i].clear();
degree[i] = ;
}
tol = ;
for (int i = ; i < n; i++) {
int u, v;
scanf("%d%d", &u, &v);
G[u].push_back(v);
degree[v]++;
}
for (int i = ; i <= n; i++)
if (!degree[i])
dfs(i, dfn2, last2);
build();
for (int i = ; i <= n; i++) {
int temp = seg.query(root[last2[i]], root[dfn2[i] - ], , n, dfn1[i], last1[i]) - ;
ans += 1LL * temp * (temp - ) / ;
}
printf("%lld\n", ans);
return ;
}

51nod1681 公共祖先的更多相关文章

  1. LCA最近公共祖先 ST+RMQ在线算法

    对于一类题目,是一棵树或者森林,有多次查询,求2点间的距离,可以用LCA来解决.     这一类的问题有2中解决方法.第一种就是tarjan的离线算法,还有一中是基于ST算法的在线算法.复杂度都是O( ...

  2. 【转】最近公共祖先(LCA)

    基本概念 LCA:树上的最近公共祖先,对于有根树T的两个结点u.v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u.v的祖先且x的深度尽可能大. RMQ:区间最小值查询问题.对于长度为n的 ...

  3. 【并查集】【树】最近公共祖先LCA-Tarjan算法

    最近公共祖先LCA 双链BT 如果每个结点都有一个指针指向它的父结点,于是我们可以从任何一个结点出发,得到一个到达树根结点的单向链表.因此这个问题转换为两个单向链表的第一个公共结点(先分别遍历两个链表 ...

  4. 洛谷P3379 【模板】最近公共祖先(LCA)

    P3379 [模板]最近公共祖先(LCA) 152通过 532提交 题目提供者HansBug 标签 难度普及+/提高 提交  讨论  题解 最新讨论 为什么还是超时.... 倍增怎么70!!题解好像有 ...

  5. Tarjan应用:求割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)【转】【修改】

    一.基本概念: 1.割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点. 2.割点集合:在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成 ...

  6. 数据结构作业——sights(最短路/最近公共祖先)

    sights Description 美丽的小风姑娘打算去旅游散心,她走进了一座山,发现这座山有 n 个景点,由于山路难修,所以施工队只修了最少条的路,来保证 n 个景点联通,娇弱的小风姑娘不想走那么 ...

  7. [最近公共祖先] POJ 3728 The merchant

    The merchant Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 4556   Accepted: 1576 Desc ...

  8. [最近公共祖先] POJ 1330 Nearest Common Ancestors

    Nearest Common Ancestors Time Limit: 1000MS   Memory Limit: 10000K Total Submissions: 27316   Accept ...

  9. 图论--最近公共祖先问题(LCA)模板

    最近公共祖先问题(LCA)是求一颗树上的某两点距离他们最近的公共祖先节点,由于树的特性,树上两点之间路径是唯一的,所以对于很多处理关于树的路径问题的时候为了得知树两点的间的路径,LCA是几乎最有效的解 ...

随机推荐

  1. [转帖]k8s 基本使用(下)

    k8s 基本使用(下) https://www.jianshu.com/p/116ce601a60f 如果你没有看过上篇的话,推荐阅读完 k8s 基本使用(上)后再阅读本篇内容. kubectl cr ...

  2. IIS 10 PHP 运行环境

    1.下载php for windows  解压到指定目录 https://windows.php.net/download/ 2.安装iis 勾选CGI 3  ,处理映射 4.添加测试站点测试是否已经 ...

  3. GO中标识符,标识符,变量,常量,iota

    参考:https://www.cnblogs.com/nickchen121/p/11517455.html 一.标识符与关键字 1.标识符 在编程语言中标识符就是程序员定义的具有特殊意义的词,比如变 ...

  4. VBA对象模型

    https://www.processon.com/view/link/5d974da6e4b07a0a4d4a098a

  5. C++强大背后

    转自MiloYip大神的博客 [原文]http://www.cnblogs.com/miloyip/archive/2010/09/17/behind_cplusplus.html 在31年前(197 ...

  6. - XML 解析 总结 DOM SAX PULL MD

    目录 目录 XML 解析 总结 DOM SAX PULL MD 几种解析方式简介 要解析的内容 DOM 解析 代码 输出 SAX 解析 代码 输出 JDOM 解析 代码 输出 DOM4J 解析 代码 ...

  7. 基于Mybatis-Plus实现自动化操作创建时间和修改时间

    引入 在实际开发中,总会避免不了操作数据库,而在数据库中每个表都会有create_time和update_time字段记录操作时间,我们在操作这两个时间的时候也可能会出现不一致的情况,或者说这两个字段 ...

  8. Kafka MirrorMaker 跨集群同步工具

    一.MirrorMaker介绍 MirrorMaker是Kafka附带的一个用于在Kafka集群之间制作镜像数据的工具.该工具从源集群中消费并生产到目标群集.这种镜像的常见用例是在另一个数据中心提供副 ...

  9. 动画重定向技术分析及其在Unity中的应用

    前言 笔者新的手游项目使用Unity引擎,动画部分要使用重定向技术来实现动画复用.笔者之前在大公司工作的时候对这块了解比较深入,读过Havok引擎在这部分的实现源码,并基于自己的理解,在公司自研的手游 ...

  10. Linux环境:VMware下windows虚拟机与linux主机进行文件共享的方法

    操作主要分两大步骤: 一.是对主机进行配置: 二.是在虚拟机上直接连接共享目录. 一.主机配置 1.打开VMware虚拟机,双击需要进行文件共享的虚拟机.如下图,双击CentOS 64位(以linux ...