51nod1681 公共祖先
[传送门]
很明显,可以转化成求每个点在两棵树中对应的子树中有多少个相同的节点,对答案的贡献就是$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 公共祖先的更多相关文章
- LCA最近公共祖先 ST+RMQ在线算法
对于一类题目,是一棵树或者森林,有多次查询,求2点间的距离,可以用LCA来解决. 这一类的问题有2中解决方法.第一种就是tarjan的离线算法,还有一中是基于ST算法的在线算法.复杂度都是O( ...
- 【转】最近公共祖先(LCA)
基本概念 LCA:树上的最近公共祖先,对于有根树T的两个结点u.v,最近公共祖先LCA(T,u,v)表示一个结点x,满足x是u.v的祖先且x的深度尽可能大. RMQ:区间最小值查询问题.对于长度为n的 ...
- 【并查集】【树】最近公共祖先LCA-Tarjan算法
最近公共祖先LCA 双链BT 如果每个结点都有一个指针指向它的父结点,于是我们可以从任何一个结点出发,得到一个到达树根结点的单向链表.因此这个问题转换为两个单向链表的第一个公共结点(先分别遍历两个链表 ...
- 洛谷P3379 【模板】最近公共祖先(LCA)
P3379 [模板]最近公共祖先(LCA) 152通过 532提交 题目提供者HansBug 标签 难度普及+/提高 提交 讨论 题解 最新讨论 为什么还是超时.... 倍增怎么70!!题解好像有 ...
- Tarjan应用:求割点/桥/缩点/强连通分量/双连通分量/LCA(最近公共祖先)【转】【修改】
一.基本概念: 1.割点:若删掉某点后,原连通图分裂为多个子图,则称该点为割点. 2.割点集合:在一个无向连通图中,如果有一个顶点集合,删除这个顶点集合,以及这个集合中所有顶点相关联的边以后,原图变成 ...
- 数据结构作业——sights(最短路/最近公共祖先)
sights Description 美丽的小风姑娘打算去旅游散心,她走进了一座山,发现这座山有 n 个景点,由于山路难修,所以施工队只修了最少条的路,来保证 n 个景点联通,娇弱的小风姑娘不想走那么 ...
- [最近公共祖先] POJ 3728 The merchant
The merchant Time Limit: 3000MS Memory Limit: 65536K Total Submissions: 4556 Accepted: 1576 Desc ...
- [最近公共祖先] POJ 1330 Nearest Common Ancestors
Nearest Common Ancestors Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 27316 Accept ...
- 图论--最近公共祖先问题(LCA)模板
最近公共祖先问题(LCA)是求一颗树上的某两点距离他们最近的公共祖先节点,由于树的特性,树上两点之间路径是唯一的,所以对于很多处理关于树的路径问题的时候为了得知树两点的间的路径,LCA是几乎最有效的解 ...
随机推荐
- [转帖]智能合约和 DApp
智能合约和 DApp https://www.jianshu.com/p/5e7df3902957 2018.10.08 19:50:41字数 3,403阅读 9,819 2017年11月份和2018 ...
- spring aop 一个挡板例子
import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.ann ...
- idea 本地调试,修改代码,代码自动生效
修改1: 修改2: ctrl+shift+a 弹出页面中搜索“registry” 修改3:修改configerations
- bootstrap使用笔记
本文翻译自[http://getbootstrap.com/2.3.2/scaffolding.html#gridSystem] 1.需要在H5的文档声明中使用: 2.如下一个简单的两列布局,先添加一 ...
- Java学习:接口(interface)的使用于注意事项
接口 接口就是一种公共的规范标准.只要符合规范标准,就可以大家通用. 接口就是多个类的公共规范.接口是一种引用数据类型,最重要的内容就是其中的:抽象方法. 如何定义一个接口的格式 如何定义一个接口的格 ...
- 【1】【leetcode-139】【回溯超时、动态规划】单词拆分
给定一个非空字符串 s 和一个包含非空单词列表的字典 wordDict,判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词. 说明: 拆分时可以重复使用字典中的单词.你可以假设字典中没有重复的 ...
- c#高效准确的条形码、线性条码、QR二维码读写类库-SharpBarcode介绍
SharpBarcode是一款支持.NET(C#,VB)的高效易用的条形码.QR二维码的读取和生成类库. 主要功能: 1.支持几乎所有常见类型的线性条形码和QR二维码的读取,高效读取,准确率高. 2. ...
- 浏览器解析js
网页加载js步骤 1.浏览器一边下载html网页,一边开始解析(不等下载完就解析)2.遇到<script>标签,暂停解析,网页渲染的控制权交给javascript引擎3.如果<scr ...
- Go的基础类型
1. 命名 Go语言中的函数名.变量名.常量名.类型名.语句标号和包名等所有的命名,都遵循一个简单的命名规则:一个名字必须以一个字母(Unicode字母)或下划线开头,后面可以跟任意数量的字母.数字或 ...
- 9.Javascript原生瀑布流
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&q ...