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

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

把每一个基环抠出来。

大力分类讨论:

  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. 1、线性DP 198. 打家劫舍

    198. 打家劫舍 https://leetcode-cn.com/problems/house-robber/ //dp动态规划,dp[i] 状态表示0-i家的盗的得最大值.那么dp[i] = (d ...

  2. spring中的数据库操作类

    例子一: package cn.itcast.service.impl; import java.util.List; import javax.sql.DataSource; import org. ...

  3. Python_Tips_dump\load 和 dumps\loads 的区别与联系

    dump\load  和 dumps\loads 的区别与联系 """ Python3 JSON模块的使用 参考链接:https://docs.python.org/3/ ...

  4. 算法:Common Subsequence(动态规划 Java 最长子序列)

    Description A subsequence of a given sequence is the given sequence with some elements (possible non ...

  5. rsync未授权访问漏洞复现

    rsync未授权访问漏洞简介 rsync是Linux/Unix下的一个远程数据同步工具,可通过LAN/WAN快速同步多台主机间的文件和目录,默认运行在873端口.由于配置不当,导致任何人可未授权访问r ...

  6. 如何在Camtasia中对录制视频添加注释

    今天我给大家带来的是一款专门录制屏幕动作的软件Camtasia,拥有了使我们的屏幕录像拥有全新的剪辑速度和更换颜色背景的特性.它不仅可以完成我们屏幕录像的心愿,还可以进行对录制的视频进行后期的编辑.这 ...

  7. Wasp XT合成器功能介绍

    本章节将采用图文结合的方式给大家讲解电音编曲软件FL Studio中的Wasp XT合成器的相关功能,感兴趣的朋友可以一起来交流哦. 下面我们一起来看看吧 Wasp XT是一个3振荡器合成器,它包含一 ...

  8. 2018-div-matrix 题解(打表)

    题目链接 题目大意 要你求有多少个满足题目条件的矩阵mod 1e9+7 \(a[1][1]=2018\;\;a[i][j]为a[i-1][j]和a[i][j-1]的因子\) 题目思路 dp也就图一乐, ...

  9. MySQL全面瓦解13:系统函数相关

    概述 提到MySQL的系统函数,我们前面有使用过聚合函数,其实只是其中一小部分.MySQL提供很多功能强大.方便易用的函数,使用这些函数,可以极大地提高用户对于数据库的管理效率,并更加灵活地满足不同用 ...

  10. Kafka分布式查询引擎

    1.概述 Kafka是一个分布式消息中间件系统,里面存储着实际场景中的数据.Kafka原生是不支持点查询的,如果我们想对存储在Topic中的数据进行查询,可能需要对Topic中的数据进行消费落地,然后 ...