[传送门]

很明显,可以转化成求每个点在两棵树中对应的子树中有多少个相同的节点,对答案的贡献就是$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. 百度前端技术学院task15源代码

    这一道题涉及到排序,读取页面内容,输出显示到某一节点当中以及添加事件. 刚开始一直在想怎么获取某一节点的内容,后面采用的是sdata.childNodes,获取所有的节点.再通过schildNode[ ...

  2. Windows ----tasklist/taskkill

    1) Tasklist命令详解 “Tasklist”命令是一个用来显示运行在本地或远程计算机上的所有进程的命令行工具,带有多个执行参数. 作用:结束一个或多个任务或进程.可以根据进程 ID 或图像名来 ...

  3. -Git 使用技巧 总结 MD

    目录 目录 Bash下的快捷操作 常用命令 常用操作 移动光标 删除输入内容 Tab键的作用 Git默认Vim编辑器基本使用 Git 使用场景 合并多个commit:rebase -i[s] 删除多个 ...

  4. ABP 用SMTP 发送163邮件

    /// <summary> /// 发送 /// </summary> /// <param name="Subject">邮件标题</p ...

  5. delegate、Action、Func的用法

    委托的特点 委托类似于 C++ 函数指针,但它们是类型安全的. 委托允许将方法作为参数进行传递. 委托可用于定义回调方法. 委托可以链接在一起. delegate的用法 delegate void B ...

  6. Micro 设计文档

    1.顶部 即时通讯 | 应用软件 | 网络学院 | 帮助中心 | 开发者中心 | 控制台 2.导航 首页 免费制作 免费下载 功能介绍 示例演示 云数据 支持与服务 免费制作:https://www. ...

  7. promiseall的使用场景

    在上图中点击诊断后下方的图标会一次进行数据请求,根据请求回来的数据显示正常异常,在请求数据完成期间再次点击诊断不触发事件 let p1 = new Promise((resolve, reject) ...

  8. 编写可维护的JavaScript-随笔(四)

    避免使用全局变量 一.全局变量带来的问题 a)      命名冲突 i.          当全局变量和全局函数越来越多时,发生命名冲突的概率也随之增高 ii.          如果函数中使用了外部 ...

  9. React学习笔记②

    import React,{Component} from 'react'; import Child from './Child.js' class App extends Component{ c ...

  10. Java语言的介绍

    1. 计算机语言 语言:沟通交流的方式 计算机语言:人与计算机之间的交流方式 java是一门计算机编程语言,也是意大利自行车品牌 软件工程师,java开发工程师 <--------------- ...