一个思路不难,但是实现起来有点毒瘤的题。

显然题目给出的是基环树(内向树)森林。

把每一个基环抠出来。

大力分类讨论:

  1. 若 \(a, b\) 不在一个联通量里,显然是 \(-1, -1\)
  2. 若 \(a, b\) 在同一颗子树内,他们聚合的点显然是最近公共祖先,因为如果再往上走,2的条件就不满足。
  3. 若 \(a, b\) 在同一联通量(同一颗基环树),两颗不同的子树内。显然他们必须走到树根,然后这时有两个选择:从 \(root[a]\) 走到 \(root[b]\),或反之。我们求出两种走的步数,按照题意进行比较即可。

第一次遇到内向树,找基环的时候不知道该如何搞。(偷瞄了一眼别人的代码)发现他是自底向上进行递归,跟往常我们写的树的自顶向下相反,学习了一下。

相比来说自底向上貌似对内向树代码简化很多(只需一次dfs),原因在于:

  1. 若自顶向下遍历,我们找到一个环的时候,可能他的子树中的某些点已经遍历过了,也就是说 \(dep\)、\(root\) 等数组都算的不对,可能需要二次 \(dfs\)。
  2. 若自底向上遍历,只有当找到环之后,一个点才会确保更新。
#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
const int N = 500005, L = 19;
int n, Q, fa[N][L], f[N], root[N], dep[N], sum[N], ult[N];
bool vis[N], ins[N];
int find(int x) { return x == f[x] ? x : f[x] = find(f[x]); }
void dfs(int u) {
vis[u] = ins[u] = true;
int v = fa[u][0];
if (ins[v]) {
// 此时从 v -> fa[v][0], ->..., -> u -> v... 构成一个环
int cnt = 0, y = v;
while (1) {
root[y] = y, dep[y] = 0, ult[y] = u;
sum[y] = ++cnt, ins[y] = false;
if (y == u) break;
y = fa[y][0];
}
return;
}
if (!vis[v]) dfs(v);
if (!root[u]) root[u] = root[v], dep[u] = dep[v] + 1, ins[u] = false;
for (int i = 1; i < L && fa[u][i - 1]; i++) fa[u][i] = fa[fa[u][i - 1]][i - 1];
}
int lca(int x, int y) {
if (dep[x] < dep[y]) swap(x, y);
for (int i = L - 1; ~i; i--)
if (dep[x] - (1 << i) >= dep[y]) x = fa[x][i];
if (x == y) return x;
for (int i = L - 1; ~i; i--)
if (fa[x][i] != fa[y][i]) x = fa[x][i], y = fa[y][i];
return fa[x][0];
}
int main() {
scanf("%d%d", &n, &Q);
for (int i = 1; i <= n; i++) f[i] = i;
for (int i = 1; i <= n; i++) scanf("%d", &fa[i][0]), f[find(i)] = find(fa[i][0]);
for (int i = 1; i <= n; i++) if (!vis[i]) dfs(i);
while(Q--) {
int a, b; scanf("%d%d", &a, &b);
if (find(a) != find(b)) puts("-1 -1");
else if(root[a] == root[b]) {
int p = lca(a, b);
printf("%d %d\n", dep[a] - dep[p], dep[b] - dep[p]);
} else {
int x = dep[a], y = dep[b], cx, cy;
if (sum[root[a]] < sum[root[b]]) cx = sum[root[b]] - sum[root[a]], cy = sum[ult[root[a]]] - cx;
else cy = sum[root[a]] - sum[root[b]], cx = sum[ult[root[a]]] - cy;
if (max(x + cx, y) < max(x, y + cy) || (max(x + cx, y) == max(x, y + cy) && (min(x + cx, y) < min(x, y + cy) || (min(x + cx, y) == min(x, y + cy) && x + cx >= y))))
printf("%d %d\n", x + cx, y);
else printf("%d %d\n", x, y + cy);
}
}
return 0;
}

AcWing 392. 会合的更多相关文章

  1. 392. Is Subsequence

    392. Is Subsequence 水题,先是判断长度,长度t比s小,返回false,然后从左到右扫描t,然后同时扫描s,如果相同,s的index就往后拉一个,如果s的index等于s长度,返回t ...

  2. 腾讯机试题 AcWing 603 打怪兽

    题目链接:https://www.acwing.com/problem/content/605/ 题目大意: 略 分析: 用dp[i][j]表示用j元钱能在前i只怪兽上所能贿赂到的最大武力值. 有一种 ...

  3. AcWing 143. 最大异或对

    https://www.acwing.com/problem/content/145 #include <iostream> #include <algorithm> usin ...

  4. AcWing 153. 双栈排序

    https://www.acwing.com/problem/content/155/ #include <cstring> #include <iostream> #incl ...

  5. acwing 3 完全背包

    习题地址 https://www.acwing.com/problem/content/description/3/ 题目描述有 N 种物品和一个容量是 V 的背包,每种物品都有无限件可用. 第 i ...

  6. AcWing 164. 可达性统计

    给定一张N个点M条边的有向无环图,分别统计从每个点出发能够到达的点的数量. 输入格式 第一行两个整数N,M,接下来M行每行两个整数x,y,表示从x到y的一条有向边. 输出格式 输出共N行,表示每个点能 ...

  7. AcWing 291.蒙德里安的梦想

    题目:蒙德里安的梦想 链接:(蒙德里安的梦想)[https://www.acwing.com/problem/content/293/] 题意:求把N * M的棋盘分割成若干个1 * 2的长方形,有多 ...

  8. acwing 861. 二分图的最大匹配 模板

    地址  https://www.acwing.com/problem/content/description/863/ 给定一个二分图,其中左半部包含n1n1个点(编号1~n1n1),右半部包含n2n ...

  9. acwing 851. spfa求最短路 模板

    地址 https://www.acwing.com/problem/content/description/853/ 给定一个n个点m条边的有向图,图中可能存在重边和自环, 边权可能为负数. 请你求出 ...

随机推荐

  1. Linux配置邮件发送信息

    背景 一般情况下,我们的IT系统都会有相关的告警的处理,有的是邮件,有的是短信,这些都能很方便的获得一些有用的信息 在某些时候我们没有这样的系统,而自己又需要定期的获取一些信息的时候,配置一个邮件发送 ...

  2. FL studio系列教程(十六):FL Studio查看菜单讲解

    FL Studio中每个窗口的显示.隐藏和布局命令都在查看菜单中.其中它被分为窗口.布局和浏览器3个部分,各项名称都有其单独的作用.窗口部分主要是软件的显示的一些菜单这里就不详细讲解了,接下来我们重点 ...

  3. 关于Java中的内存屏障

    如何打破双亲委派机制 继承ClassLoader类后重写loadClass方法 如何指定自定义ClassLoader中的parent 默认parent是appClassLoader,可以通过Class ...

  4. vulnhub: DC 3

    通过nmap扫描,只开放了80端口,并且该web服务是基于Joomla搭建: root@kali:~# nmap -A 192.168.74.140 Starting Nmap 7.80 ( http ...

  5. Win10定期执行python程序

    一:windows10自带的计划程序 第一步:在 计算器右击 --> 选择管理 进入如下界面: 第二步:选择 系统工具 -->  任务计划程序 ,点击右侧的  "创建基本任务&q ...

  6. 关于transition动画效果中,滚动条会闪一下就消失的问题

    具体问题说明: 我在通过transition来改变width的长度,在transition变化过程中,底下的滚动条会闪烁一下. 问题原理:因为是里面容器没办法完全被装下,并且容器的宽度被限制住了. 解 ...

  7. Guava中EventBus分析

    EventBus 1. 什么是EventBus 总线(Bus)一般指计算机各种功能部件之间传送信息的公共通信干线,而EventBus则是事件源(publisher)向订阅方(subscriber)发送 ...

  8. Bootstrap Blazor 组件介绍 Table (二)自定义模板列功能介绍

    Bootstrap Blazor 是一套企业级 UI 组件库,适配移动端支持各种主流浏览器,已经在多个交付项目中使用.通过本套组件可以大大缩短开发周期,节约开发成本.目前已经开发.封装了 70 多个组 ...

  9. 卡耐基梅隆大学(CMU)元学习和元强化学习课程 | Elements of Meta-Learning

    Goals for the lecture: Introduction & overview of the key methods and developments. [Good starti ...

  10. MSSQL 2014数据库Alwayson下日志过大,压缩日志的处理方法

    USE [{数据库名称}] DECLARE @bakfile nvarchar(100) SET @bakfile='E:\DbLogs\log_bak_'+CONVERT(nvarchar(8),G ...