[ZJOI2018]迷宫

复盘 cdw 讲的题,写篇题解来祸害社会

Description

给定 \(m\)\(k\) ,要求构造这样的一张图,满足以下条件:

  • 每个点恰好有 \(m\) 条出边,每个出边都恰好有一个转移值 \(\in [0, m)\)

  • 从初始点开始,走一条长度为 \(k\) 的路径,并且回到出发节点,路径上所有转移构成的一个 \(m\) 进制的数恰为 \(k\) 的倍数。

  • 图无限制,可有自环和重边。

要求一个总点数最少的图,求这个点数 \(n\)

多测。

\(1\leq m,\ k \leq 10 ^ {18},\ 1\leq t \leq 3 \cdot 10 ^ 5\)

Solution

能明显感觉到这玩意的定义有点像 DFA ,但是用处不大所以我们不去管他。

n = k

(注:初始节点定为 \(0\) ,对于状态表示更方便

先满足第一个限制,这样就有一个有着无限个点的 \(m\) 叉树,每条边能够按照题目的意思分配好转移值,那么每个点对应的状态就是从初始状态到自己的路径组成的 \(m\) 进制数。

形式化表达就是对于一个点对应的状态 \(u\) ,儿子的状态为 \(u \cdot m + i,\ i \in [0,\ m)\)

考虑怎么一步到位,满足第二个限制,我们知道,如果要求一个数是 \(k\) 的倍数,那么在树上的 \(m\) 个儿子在模 \(k\) 意义下本质上还是一样的,那么我们就直接考虑对所有状态的值模 \(k\)

恰好所有 \(k\) 的倍数最终会连回初始节点同时因为有限制三,我们无论如何都不会连的不合法,所以这样我们就一次性得到了一个点数为 \(k\) 的满足条件的图。

n < k

这里同 SAM 一样,我们要求的是一个点数尽量少的图,所以我们也要考虑考虑缩点的问题。

假如两个点 \(a\)\(b\) 能被缩到一起,那么一定有:

  • \(\forall\ i \in [0,\ m),\ (a \cdot m + i) \equiv (b \cdot m + i)\ ({\rm mod}\ k)\)

或者

  • \(\forall\ i \in [0,\ m),\ (a \cdot m + i) \equiv b\ ({\rm mod}\ k)\)

再或者

  • \(\forall\ i \in [0,\ m),\ (b \cdot m + i) \equiv a\ ({\rm mod}\ k)\)

形式化理解就是要么这两个状态全部指向同样的点,要么无论从哪个点进入状态 \(1\) ,它都会给你怼到状态 \(2\) 去,这样的话明显我们不需要建立两个点,就可以缩掉。

其实我们还可以进一步感受到一个地方就是上文提到的后两种情况,因为 \(i\) 是连续的整数,所以只有可能是 \(k = 1\) 的情况,这种情况又只有 \(0\) 它自己,所以说可以忽略。

所以我们就只有这样的一个限制:

\[\forall\ i \in [0,\ m),\ (a \cdot m + i) \equiv (b \cdot m + i)\ ({\rm mod}\ k) \]

\[\Rightarrow a \cdot m \equiv b \cdot m\ ({\rm mod}\ k) \]

此时 \(m\)\(k\) 可能不互质,我们令 \(g = \gcd(m, k),\ k^{'} = k / g,\ m^{'} = m / g\) ,有。

\[\Rightarrow a \cdot m^{'} \equiv b \cdot m^{'}\ ({\rm mod}\ k^{'}) \]

此时则必然有 \(a \equiv b\ ({\rm mod}\ k ^ {'})\) ,具体证明(其实不是很重要,意会意会就行了):

使 \(a\)\(b\) 能表示成: \(a = i \cdot k ^ {'} + x,\ b = j \cdot k ^ {'} + y\) 其中 \(x\)\(y\) 均小于 \(k ^ {'}\)
则有: \(a \equiv x\ ({\rm mod}\ k ^ {'}),\ b \equiv y\ ({\rm mod}\ k ^ {'})\)
同时也有:\(a \cdot m ^ {'} \equiv (i \cdot k ^ {'} + x) \cdot m ^ {'} \equiv x \cdot m ^ {'}\ ({\rm mod}\ k ^ {'}),\ b \cdot m ^ {'} \equiv (j \cdot k ^ {'} + y) \cdot m ^ {'} \equiv y \cdot m ^ {'}\ ({\rm mod}\ k ^ {'})\)
所以:\(x \cdot m ^ {'} \equiv y \cdot m ^ {'}\ ({\rm mod}\ k ^ {'})\)
因为注意到 \(x\)\(y\) 的范围不超过 \(k ^ {'}\) ,所以有:\(x \equiv y\ ({\rm mod}\ k ^ {'})\)
所以:\(a \equiv b\ ({\rm mod}\ k ^ {'})\)

所以很明显在新的模 \(k ^ {'}\) 的意义下相同的数都要合并到一起,然后你手模了三个样例,发现全部满足,然后就结束了吗??

\(m = 2,\ k = 8\) 的时候,手摸完发现点数变成了 \(5\) 个,但是似乎还可以继续合并成 \(4\) 个点。但是在刚刚的步骤中仅仅只操作了一次,所以这个做法并不完美。

那怎么处理能够多次缩点的情况呢??

其实我们能感觉到这有点子问题的意思,在这些缩完的点中,其实就是在新的 \(k ^ {'}\) 之下一个新的图,所以按照之前的做法,我们可以总结出:\(a \cdot m^p \equiv b \cdot m^p\ ({\rm mod}\ k^{'})\) ,而每次我们只需要把新的 \(k\) 传下去做递归,直到不能再缩为止。

注意到可能相乘会爆 long long ,所以乘化除就行了。

Code

/*

*/
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll m, k;
inline ll read() {
	char ch = getchar();
	ll s = 0, w = 1;
	while (!isdigit(ch)) {if (ch == '-') w = -1; ch = getchar();}
	while (isdigit(ch)) {s = (s << 3) + (s << 1) + (ch ^ 48); ch = getchar();}
	return s * w;
}
inline ll gcd(ll a, ll b) {return (!b) ? a : gcd(b, a % b);}
inline ll solve(ll now, ll t, ll k) {
	ll g = gcd(m, k), _k = k / g, _m = m / g;
	if (g == 1 || now <= _k) return 0;
	if ((double)_k / _m < t) return now - _k;
	t *= _m;
	return solve(_k - t, t, _k) + now - _k;
}
inline void mian() {
	m = read(); k = read();
	printf("%lld\n", k - solve(k - 1, 1, k));
}
int main() {
	int T = read();
	while (T--) mian();
	return 0;
}
posted @ 2022-02-13 09:50  Illusory_dimes  阅读(101)  评论(1编辑  收藏  举报