[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\\) 它自己,所以说可以忽略。

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

\\[\\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 ,所以乘化除就行了。

/*

*/
#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]迷宫的主要内容,如果未能解决你的问题,请参考以下文章

zjoi[ZJOI2018]胖

bzoj 5308 [ZJOI2018] 胖

「ZJOI2018」历史

ZJOI2018酱油记

ZJOI2018游记 [一试]

[蓝桥杯2018决赛]迷宫与陷阱