题解 P4339 【[ZJOI2018]迷宫】
这篇文章的所有图片使用
初稿 on 2020-05-28 16:26:54
更改图源 on 2021-03-21 10:25:22
构造有 K 个点的“基本方案”
考虑构造一个有
为了方便,我们假设题目中的
对于一个点
为了使得路径能回到
于是我们容易想到,如果把每个节点的编号都模上
整理一下:
为了得到一个有
“缩点”
我们发现,样例中第一组数据是满足我们的构造方案的,但是第二、三组数据的答案均比
考虑用我们的方案先构造一个
其中黑色边表示
我们能发现什么东西么?
基本事实 1 如果两个点可以到达的点集相同,那么这两个点可以被缩成 1 个点。
所谓“可以到达的点集相同”,形式化的说,对于两个点
感性理解,就是缩点之后,
如样例第二组数据,可以对
可以发现,这就是样例解释。
(
再给一张大一点的图(对应样例第三组数据)。
黑色边表示
当
缩点后得到一个
可以手玩一下验证其正确性
即样例输出
关于 0 号节点
注意到上面几张图中
注意到这个节点比较的特殊,不能被缩点。
为什么?
因为
但是其他与
口说无凭,以图解释。
缩点后
什么时候可以缩点
对于两个可以缩的点
因为
所以若有
设
则有
设
则有
此时
那么若有
证明:
不妨设a=cK^′+e,b=dK^′+f
其中e,f\in[0,K^′) 且c,d,e,f 均为自然数\therefore a\equiv e\pmod {K^′},b\equiv f\pmod {K^′} \because a·m^′\equiv m^′(cK^′+e)\equiv m^′cK^′+m^′e\equiv m^′e\pmod {K^′}, b·m^′\equiv m^′(dK^′+f)\equiv m^′dK^′+m^′f\equiv m^′f\pmod {K^′} \therefore m^′e=m^′f\pmod {K^′} \because m^′,K^′$ 互质且 $a\not\equiv 0 \pmod {K^′}$, $b\not\equiv 0 \pmod {K^′} \therefore e=f \therefore a\equiv b\pmod {K^′} 因此原命题得证
考虑计算哪些点可以缩点。
对于点
一个点可以被缩点,当且仅当
此时能与点
而对于
因此,对于同一个
因为
所以最终答案为
发现这一性质满足样例的三组数据。
提交后,我们获得了
论证之不严密
我们发现,有些点是可以被二次缩点的。
观察下列
基本方案
一次缩点
二次缩点
这时候我们的原计算方案就失效了,需要寻求新的解决方案。
递归缩点
由于需要多次缩点,考虑递归求解。
发现两个点
不妨设当前缩点后还剩下
需要注意,在前一轮缩点中没有被缩点的点,下一轮一定不可能被缩点(自行感性理解,或者结合上面
考虑设计一个函数 solve,返回缩点中去除了几个节点。
分析这个函数需要的参数。
由于每次一定进行了缩点,
同时需要参数
还需要参数
同时由于我们下传的
考虑到如果
考虑到如果
因此上述两种情况都应该返回
考虑到缩点的时候,缩得的 solve 的
考虑递归终止条件。
假设
但是 如果这样计算,int64(实测
(事实上,你可以使用 __int128 解决问题,但不能保证 ZJOI i3 老年机会不会返回
solve 的返回值应为下一层递归的结果加上
代码放 solve 函数和 main 函数
其中 K_ 表示 m_ 表示
typedef long long int64;
int64 solve(int64 r, int64 t, int64 K) {
int64 g = gcd(m, K), K_ = K / g, m_ = m / g;
if (g == 1 || r <= K_) return 0;
if ((double)K_ / m_ < t) return r - K_;
t *= m_;
return solve(K_ - t, t, K_) + r - K_;
}
int main()
{
T = read<int>();
while (T--) {
m = read<int64>();
K = read<int64>();
int64 g = gcd(m, K), K_ = K / g;
write(K - solve(K - 1, 1, K), 10);
}
return 0;
}