[ZJOI2018]迷宫
Posted Illu的小窝
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[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\\) 它自己,所以说可以忽略。
所以我们就只有这样的一个限制:
此时 \\(m\\) 和 \\(k\\) 可能不互质,我们令 \\(g = \\gcd(m, k),\\ k^\' = k / g,\\ m^\' = m / g\\) ,有。
此时则必然有 \\(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 ,所以乘化除就行了。
/*
*/
#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;
以上是关于[ZJOI2018]迷宫的主要内容,如果未能解决你的问题,请参考以下文章