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是几乎最有效的解 ...
 
随机推荐
- 029 ElasticSearch----全文检索技术04---基础知识详解02-查询
			
1.查询 (1)基本查询 基本语法: GET /索引库名/_search { "query":{ "查询类型":{ "查询条件":" ...
 - 第八节:Asp.Net Core整合Log4net(官方的、微软的两种)
			
一. 整合Log4net 1. 简单说明 对于log4net 官方的程序集而言,从2.0.7开始就支持.Net Core了,这里我们采用的是2.0.8,虽然好久没更新了,但不影响使用.Core版本与普 ...
 - js实现对上传图片的路径转成base64编码,并且对图片进行压缩,实现预览功能1
			
参考 https://blog.csdn.net/qq_31965515/article/details/82975381 https://www.cnblogs.com/zhangdiIT/p/78 ...
 - Java基础知识点总结(一)
			
1.源文件声明规则2.JAVA基本类型void3.数据类型默认值4.自动类型转换5.Java变量类型6.Java局部变量7.访问控制修饰符8.父类与子类的访问控制9.instanceof运算符 1.源 ...
 - Python做一个计时器的动画
			
一.问题在做连连看的时候需要加一个计时器的动画,这样就完成了计时功能的设计. 二.解决主要思路: 1.先产生一个画布,用深颜色填充满. 2.产生一个新的矩阵用来覆盖画布,背景用白色,就可以渲染出来递减 ...
 - 信安周报-第03周:DB系统表
			
信安之路 第03周 前言 这周自主研究的任务如下: 任务附录的解释: 文件读写在通过数据库注入漏洞获取webshell的时候很有用 系统库和表存放了很多关键信息,在利用注入漏洞获取更多信息和权限的过程 ...
 - C# Winform控件字体大小自适应
			
using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; namespace WGClie ...
 - 【转】Webpack 快速上手(下)
			
由于文章篇幅较长,为了更好的阅读体验,本文分为上.中.下三篇: 上篇介绍了什么是 webpack,为什么需要 webpack,webpack 的文件输入和输出 中篇介绍了 webpack 在输入和输出 ...
 - 使用EF Core+CodeFirst建立ASP.NET Core MVC项目
			
本篇随笔介绍如何使用.NET Core+EF Core创建Web应用程序 首先借用官网的话简单介绍一下ASP.NET Core ASP.NET Core 是一个跨平台的高性能开源框架,用于生成基于云且 ...
 - Java检查字符串是否包含中文字符
			
转自:https://blog.csdn.net/zhanghan18333611647/article/details/80038629 强烈推荐一个大神的人工智能的教程:http://www.ca ...