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是几乎最有效的解 ...
随机推荐
- OI之路
由于各种因素 学习.初赛.时间... 我的oi可能会结束, 我也不甘于放弃, 所以 综合今后的表现, 我再决定 以后我尽量写博客.
- texstudio基本设置
一开始默认为英文,在上面菜单栏,“option” 1.设置中文:options->general->language->zh-cn 2.编辑和查看按钮: 3.设置默认编译器:选项-& ...
- 阿里云服务器连接ftp服务(软件的使用)
首先你需要有一个阿里云的ECS服务器 开通了宽带之后,ECS服务器就可以上网了 可以在本地电脑cmd控制台运行mstsc.exe启动远程桌面连接 windows+R 计算机名输入ECS服务器的公网ip ...
- 一个简单便捷的树形显示Ztree
这是本人在闲时研究的一个用于显示树形列表的小玩意. zTree 是一个依靠 jQuery 实现的多功能 “树插件”.优异的性能.灵活的配置.多种功能的组合是 zTree 最大优点. 下面就说说怎么用吧 ...
- sql server 根据字段去重
使用 row_number() over (partition by 要去重的字段 order by 排序字段) 数据库表结构 学生成绩表 UserGrade Id int ...
- JVM故障分析系列之四:jstack生成的Thread Dump日志线程状态
JVM故障分析系列之四:jstack生成的Thread Dump日志线程状态 2017年10月25日 Jet Ma JavaPlatform JVM故障分析系列系列文章 JVM故障分析系列之一: ...
- 关于BASE 24 ,BASE 64原理以及实现程序
关于BASE 24 ,BASE 64原理以及实现程序 来源 https://wangye.org/blog/archives/5/ 可能很多人听说过Base64编码,很少有人听说过Base24编码,B ...
- Django---FORM组件.FORM组件的字段,FORM组件校验流程,FORM组件的全局和局部钩子,FORM和Model的组合
Django---FORM组件.FORM组件的字段,FORM组件校验流程,FORM组件的全局和局部钩子,FORM和Model的组合 一丶FORM的介绍 1.生成页面可用的HTML标签 2.对用户提交的 ...
- 【JVM学习笔记二】垃圾收集器与内存分配策略
1. 概述 1) GC的历史比Java久远 2) GC需要完成的三件事: | 哪些内存需要回收 | 什么时候回收 | 如何回收 3) Java内存运行时区域各个部分: | Java虚拟机栈.计数器.本 ...
- web和网络基础
TCP/IP 协议族按层次分别分为以下 4 层: 应用层. 传输层. 网络层和数据链路层 把 TCP/IP 层次化是有好处的. 比如, 如果互联网只由一个协议统筹, 某个地方需要改变设计时, 就必须把 ...