0%

2025 CCPC 全国邀请赛(南昌)暨第二届江西省赛——D. 挑战图同构(分量哈希)

思路讲解

当然,我们首先看到这道题目,会想到暴力。

但是暴力显然不行,先不管这个东西要怎么求,存储上,你存都存不下来。

既然这个暴力是不行的,我们就想到,这个图上面的路径太多了,有什么东西两点之间只有一条路径呢?其实就是树。

那么这种比较复杂的题目,特别是比较复杂的图论题目,一般都要划分阶段。

因此我们就要想,这个,如何把这节点一点一点的加入这个图当中?

可以的,兄弟,是可以的。

我们假设我们现在有一张图,我们可以先把到这张图距离为 i 的点加入进来,再把 i+1 的点加入进来,… 。

我们可以使用 kruskal 进行实现,kruskal 的基本过程就是按从小到大排序,加入到图中,使用并查集判环。

划分阶段其实也很简单,就是两张图把距离为 i 的点加入进来,停住。干一些事情,再继续加入距离为 i+1 的点,… 。

我们把一个中间状态摘出来看,如果原来的图是一样的(同构的),加入进来的点的并查集划分是一样的,那么两张图就是同构的。

这么说有点抽象。比如说蓝色的点是原来的图,红色的点是新加进来的点,不难发现,这两张图是同构的。因为只要并查集划分相同,新点之间的这个 dis 就是 i,老点到新点之间的 dis 也都是 i。

image

然后我们就来到了最后一个问题,怎么样快速判断( O(1)O(1) )两个图的并查集划分是相同的那?呃,其实不是那么难的。我们请出我们的老盆友,哈希。

一个联通块的签名可以理解为 有什么点。有什么点的话,我们可以给每个点整一个随机值(mt19937_64),然后一个联通块有什么点,就相当于其所有拥有的点的随机值的异或起来。

把这个好几个联通块组合(异或)起来,就可以得到目前的这个并查集划分状态,然后直接比较这个哈希值即可。

AC代码

https://qoj.ac/submission/1415583

心路历程(WA,TLE,MLE……)

merge 的逻辑比较复杂,容易写错,之后可以使用,Old,New,这样的命名,避免混淆。

1
2
3
4
5
6
7
8
9
10
11
void merge(ll a, ll b) {
ll faa = find(a), fab = find(b);
if (faa == fab) return;
S ^= mix(Sum[faa], siz[faa]);
S ^= mix(Sum[fab], siz[fab]);
fa[faa] = fab;
siz[fab] += siz[faa];
Sum[fab] ^= Sum[faa];
S ^= mix(Sum[fab], siz[fab]);
return;
}