P1587 [NOI2016] 循环之美

该问题讨论了如何确定在给定进制下,有多少个纯循环小数可以用特定形式的分数表示。通过莫比乌斯反演和数学推理,可以计算出这些美的数的个数。题目提供了详细的转换方法和样例解释,并给出了数据范围和测试点的限制。
部署运行你感兴趣的模型镜像

题目描述

牛牛是一个热爱算法设计的高中生。在他设计的算法中,常常会使用带小数的数进行计算。牛牛认为,如果在 �k 进制下,一个数的小数部分是纯循环的,那么它就是美的。现在,牛牛想知道:对于已知的十进制数 �n 和 �m,在 �k 进制下,有多少个数值上互不相等的纯循环小数,可以用分数 ��yx​ 表示,其中 1≤�≤�,1≤�≤�1≤x≤n,1≤y≤m,且 �,�x,y 是整数。一个数是纯循环的,当且仅当其可以写成以下形式:

�.�1˙�2�3…��−1��˙a.c1​˙​c2​c3​…cp−1​cp​˙​

其中,�a 是一个整数,�≥1p≥1;对于 1≤�≤�1≤i≤p,��ci​ 是 �k 进制下的一位数字。

例如,在十进制下,0.45454545……=0.4˙5˙0.45454545……=0.4˙5˙ 是纯循环的,它可以用 511115​、10222210​ 等分数表示;在十进制下,0.1666666……=0.16˙0.1666666……=0.16˙ 则不是纯循环的,它可以用 1661​ 等分数表示。需要特别注意的是,我们认为一个整数是纯循环的,因为它的小数部分可以表示成 00 的循环或是 �−1k−1 的循环;而一个小数部分非 00 的有限小数不是纯循环的。

输入格式

只有一行,包含三个十进制数 �,�,�N,M,K 意义如题所述。

输出格式

一行一个整数,表示满足条件的美的数的个数。

输入输出样例

输入 #1复制

2 6 10

输出 #1复制

4

说明/提示

样例解释

满足条件的数分别是:

11=1.0000…11​=1.0000…

13=0.3333…31​=0.3333…

21=2.0000…12​=2.0000…

23=0.6666…32​=0.6666…

1111​ 和 2222​ 虽然都是纯循环小数,但因为它们相等,因此只计数一次;同样,1331​ 和 2662​ 也只计数一次。

数据范围

对于所有的测试点,保证 1≤�≤1091≤n≤109,1≤�≤1091≤m≤109,2≤�≤2×1032≤k≤2×103。

对于每个测试点,有以下约束(其中留空的表示没有特殊的约束):

测试点编号�n�m�k
11≤10≤10≤20≤20=2=2
22≤100≤100≤104≤104=2=2
33≤103≤103=2=2
44≤104≤104=2=2
55≤10≤10≤20≤20=3=3
66≤100≤100≤104≤104=3=3
77≤103≤103=3=3
88≤104≤104=3=3
99≤10≤10≤20≤20≤100≤100
1010≤100≤100≤104≤104≤100≤100
1111≤103≤103≤103≤103
1212≤104≤104
1313≤105≤105≤108≤108≤100≤100
1414≤2×105≤2×105≤103≤103
1515≤5×105≤5×105
1616≤106≤106≤108≤108≤100≤100
1717≤2×106≤2×106≤103≤103
1818≤5×106≤5×106
1919≤107≤107≤108≤108100100
2020≤2×107≤2×107≤103≤103
2121≤2×107≤2×107
2222≤108≤108≤108≤108
2323≤108≤108≤108≤108
24,2524,25

提示

这部分将提供一个将分数化为对应的小数的方法,如果你已经熟悉这个方法,你不必阅读本提示。

分数可以通过除法,用分子除以分母化为对应的小数。有些分数在除法过程中无法除尽,这样的分数在不断进行的除法过程中余数一定会重复出现。从商数的个位所对应的余数起,设第一次重复出现的余数前两次出现的位置所对应的商数位分别是小数点后第 �a 位和小数点后第 �b 位(特殊地:如果其中一个对应的商数位是个位,则认为 �=0a=0;不妨设 �<�a<b),则其循环部分可以用小数点后第 �+1a+1 位到小数点后第 �b 位的循环来表示。

例如:在十进制下,将 511115​ 转化为小数时,个位开始的商数依次为 4,5,4,…4,5,4,…,对应的余数分别为 6,5,6,…6,5,6,…。余数第一次重复出现的位置是个位和小数点后第 22 位,那么 �=0,�=2a=0,b=2。

�=0,�=2a=0,b=2 即其循环部分可以用小数点第 11 位到第 33 位来表示。表示为:511=0.45454545…=0.4˙5˙115​=0.45454545…=0.4˙5˙。

在十进制下,将 1661​ 转化为小数时,个位开始的商数依次为 1,6,6,…1,6,6,…,对应的余数分别为 4,4,4,…4,4,4,…。余数第一次重复出现的位置是小数点后第 11 位和小数点后第 22 位,即其循环部分可以用小数点后第 22 位来表示。表示为:16=0.1666……=0.16˙61​=0.1666……=0.16˙。

需要注意的是:商数重复出现并不代表进入了循环节。

题解:

首先是判断分数��yx​是否为纯循环小数的方法,

根据打表可以得到结论:

​ �K进制下,��yx​为纯循环小数当且仅当�y与�k互质。

证明:

�K进制下,��yx​为纯循环小数,则有

�∗��≡�(��� �)(�≠0)x∗kl≡x(mod y)(l=0)

两边同除�x.

��≡1(��� �)kl≡1(mod y)

得�,�k,y互质。

证毕。

题目转化为求

∑�=1�∑�=1�[���(�,�)==1][���(�,�)==1]x=1∑n​y=1∑m​[gcd(x,y)==1][gcd(y,k)==1]

∑�=1�∑�=1�[���(�,�)==1][���(�,�)==1]x=1∑n​y=1∑m​[gcd(y,k)==1][gcd(x,y)==1]

更换枚举顺序

∑�=1�[���(�,�)==1]∑�=1�[���(�,�)==1]y=1∑m​[gcd(y,k)==1]x=1∑n​[gcd(x,y)==1]

莫比乌斯反演得

∑�=1�[���(�,�)==1]∑�=1�∑�∣���(�,�)�(�)y=1∑m​[gcd(y,k)==1]x=1∑n​d∣gcd(x,y)∑​μ(d)

枚举约数�d得

∑�=1��(�)∑�∣��∑�∣��[���(�,�)==1]d=1∑n​μ(d)d∣x∑n​d∣y∑m​[gcd(y,k)==1]

∑�=1�[���(�,�)==1]�(�)∑�=1⌊��⌋∑�=1⌊��⌋[���(�,�)==1]d=1∑n​[gcd(d,k)==1]μ(d)x=1∑⌊dn​⌋​y=1∑⌊dm​⌋​[gcd(y,k)==1]

∑�=1�[���(�,�)==1]�(�)⌊��⌋∑�=1⌊��⌋[���(�,�)==1]d=1∑n​[gcd(d,k)==1]μ(d)⌊dn​⌋y=1∑⌊dm​⌋​[gcd(y,k)==1]

令�(�)=∑�=1�[���(�,�)==1],�(�,�)=∑�=1�[���(�,�)==1]�(�)f(x)=i=1∑x​[gcd(i,k)==1],s(x,k)=i=1∑x​[gcd(i,k)==1]μ(i).

考虑求�(�)f(x),若�i与�k互质,则有�+�i+k与�k互质。

所以 �(�)=⌊��⌋�(�)+�(�f(x)=⌊kx​⌋f(k)+f(x % �)k)

预处理�(�)f(x)满足1≤�≤�1≤x≤k的�(�)f(x),剩下的递归处理即可。

考虑求�(�,�)s(x,k).

�(�,�)=∑�=1�[���(�,�)==1]�(�)s(x,k)=i=1∑x​[gcd(i,k)==1]μ(i)

=∑�=1��(�)[���(�,�)==1]=i=1∑x​μ(i)[gcd(i,k)==1]

=∑�=1��(�)∑�∣���(�,�)�(�)=i=1∑x​μ(i)d∣gcd(i,k)∑​μ(d)

=∑�=1��(�)∑�∣�,�∣��(�)=i=1∑x​μ(i)d∣i,d∣k∑​μ(d)

=∑�∣��(�)∑�∣��(�)=d∣k∑​μ(d)d∣i∑​μ(i)

=∑�∣��(�)∑�=1⌊��⌋�(�∗�)=d∣k∑​μ(d)i=1∑⌊dx​⌋​μ(i∗d)

若���(�,�)≠1gcd(i,d)=1时,�(�∗�)=0μ(i∗d)=0,对答案没有贡献.

由积性函数的性质�(��)=�(�)∗�(�)f(ab)=f(a)∗f(b)得

=∑�∣��(�)∑�=1⌊��⌋[���(�,�)==1]�(�)�(�)=d∣k∑​μ(d)i=1∑⌊dx​⌋​[gcd(i,d)==1]μ(i)μ(d)

=∑�∣��(�)2∑�=1⌊��⌋[���(�,�)==1]�(�)=d∣k∑​μ(d)2i=1∑⌊dx​⌋​[gcd(i,d)==1]μ(i)

=∑�∣��(�)2∑�=1⌊��⌋[���(�,�)==1]�(�)=d∣k∑​μ(d)2i=1∑⌊dx​⌋​[gcd(i,d)==1]μ(i)

=∑�∣��(�)2∗�(⌊��⌋,�)=d∣k∑​μ(d)2∗s(⌊dx​⌋,d)

递归处理即可。

但当�=1k=1时,�(�,�)s(x,k)的值无法通过递归得出。

考虑�=1k=1的情况,

�(�,1)=∑�=1�[���(�,1)==1]�(�)s(x,1)=i=1∑x​[gcd(i,1)==1]μ(i)

=∑�=1��(�)=i=1∑x​μ(i)

由于1≤�≤1�81≤x≤1e8,杜教筛求前缀和即可。

题目中所求的

���=∑�=1�[���(�,�)==1]�(�)⌊��⌋∑�=1⌊��⌋[���(�,�)==1]ans=d=1∑n​[gcd(d,k)==1]μ(d)⌊dn​⌋y=1∑⌊dm​⌋​[gcd(y,k)==1]

整除分块处理。

code:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <map>
#include <algorithm>
#define int long long
using namespace std;
const int N = 1e6 + 5;
int read() {
	int x = 0, f = 1; char ch;
	while(! isdigit(ch = getchar())) (ch == '-') && (f = -f);
	for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 3) + (x << 1) + (ch ^ 48));
	return x * f;
}
map <pair <int, int>, int> mp;
int n, m, K, ans;
int tot, gcd_k[N], f[N], mu[N], Smu[N], vis[N], pri[N];
void init() {
	for(int i = 1; i <= K; ++ i) gcd_k[i] = __gcd(i, K) == 1;
	for(int i = 1; i <= K; ++ i) f[i] = f[i - 1] + gcd_k[i];
	mu[1] = 1;
	for(int i = 2; i < N; ++ i) {
		if(! vis[i]) pri[++ tot] = i, mu[i] = -1;
		for(int j = 1; j <= tot && i * pri[j] < N; ++ j) {
			vis[i * pri[j]] = 1;
			if(i % pri[j] == 0) break;
			mu[i * pri[j]] = - mu[i];
		}
	}
	for(int i = 1; i < N; ++ i) Smu[i] = Smu[i - 1] + mu[i];
}
int Sf(int x) {
	return (x / K) * f[K] + f[x % K];
}
int Ss(int x, int k){
	if((k == 1 && x <= N) || x == 0) return Smu[x];
	if(mp[make_pair(x, k)]) return mp[make_pair(x, k)];
	int res = 0;
	if(k == 1) {
		res = 1;
		for(int i = 2, j; i <= x; i = j + 1) {
			j = x / (x / i);
			res -= (j - i + 1) * Ss(x / i, k);
		}
	}
	else {
		for(int i = 1; i * i <= k; ++ i) {
			if(k % i) continue;
			if(mu[i]) res += Ss(x / i, i);
			if(i * i != k && mu[k / i]) {
				res += Ss(x / (k / i), k / i);
			}
		}
	}
	return mp[make_pair(x, k)] = res;
}
signed main() {
	n = read(); m = read(); K = read();
	init();
	for(int i = 1, j, nw = 0, lst = 0; i <= min(n, m); i = j + 1) {
		j = min(n / (n / i), m / (m / i));
		nw = Ss(j, K);
		ans = ans + (nw - lst) * (n / i) * Sf(m / i);
		lst = nw;
	}
	printf("%lld\n", ans);
	return 0;
}

您可能感兴趣的与本文相关的镜像

Stable-Diffusion-3.5

Stable-Diffusion-3.5

图片生成
Stable-Diffusion

Stable Diffusion 3.5 (SD 3.5) 是由 Stability AI 推出的新一代文本到图像生成模型,相比 3.0 版本,它提升了图像质量、运行速度和硬件效率

P5997【NOI2025】数字树 NOI/NOI+/CTSC NOI 标准IO 传统题 来源 OJ小柴 时间限制 4000ms 内存限制 1024MB 通过/尝试次数 55/143 题目背景 NOI2025真题 题目描述 给定一棵 4n − 1 个结点的二叉树,其中每个非叶结点都有恰. 好. 两个子结点。非叶 结点编号为 1 到 2n − 1,叶子结点编号为 2n 到 4n − 1。初始时,每个叶子结点上都没 有数字。 定义一个 DFS 序是优. . 的. ,当且仅当按该 DFS 序将所. 有. 标. 有. 数. 字. 的. 叶. 子. 结. 点. 上的 数字拼成一个序列时,该序列可以通过若干次消. 除. 相. 邻. 相. 同. 数. 字. 的方式得到空序列。例 如,在下图中,若叶子结点 4, 6 上标有数字 1,叶子结点 5, 7 上标有数字 2,则按 DFS 序 [1, 4, 2, 7, 3, 5, 6] 将所有标有数字的叶子结点上的数字拼成的序列为 [1, 2, 2, 1],可以通过 消除相邻的 2 的方式得到 [1, 1],再通过消除相邻的 1 的方式得到空序列,因此该 DFS 序是优的;而按 DFS 序 [1, 4, 2, 3, 5, 6, 7] 将所有标有数字的叶子结点上的数字拼成的 序列为 [1, 2, 1, 2],无法通过若干次消除相邻相同数字的方式得到空序列,因此该 DFS序不是优的 给定 n 次操作,第 i (1 ≤ i ≤ n) 次操作会选择两个没. 有. 数. 字. 的叶子结点,然后将这两个结点标上数字 i。保. 证. 在. 每. 次. 操. 作. 后,. 存. 在. 至. 少. 一. 个. 优. . 的. DFS 序。. 你需要求出每次操作后的优的 DFS 序的数量。由于答案可能较大,你只需要求出答案对1, 000, 000, 007 取模后的结果 输入格式 从文件 tree.in 中读入数据。 输入的第一行包含一个非负整数 c,表示测试点编号。c = 0 表示该测试点为样例。 输入的第二行包含一个正整数 n,表示二叉树的结点个数为 4n − 1 输入的第 i + 2 (1 ≤ i ≤ 2n − 1) 行包含两个正整数 li 和 ri,分别表示结点 i 的左右子结点。保证 i < li, ri ≤ 4n − 1,且所有的 li, ri 互不相同。 输入的第 i + 2n + 1 (1 ≤ i ≤ n) 行包含两个正整数 ai, bi,表示第 i 次操作选择的叶子结点的编号。保证 2n ≤ ai, bi ≤ 4n − 1,且所有的 ai, bi 互不相同 输出格式 输出到文件 tree.out 中。 输出 n 行,其中第 i (1 ≤ i ≤ n) 行包含一个非负整数,表示第 i 次操作后的优的 DFS 序的数量对 1, 000, 000, 007 取模后的结果。 输入 #1 复制 0 2 4 2 3 7 5 6 4 6 5 7 输出 #1 复制 8 4 输入 #2 复制 0 6 2 3 4 21 22 23 5 11 6 8 7 9 12 13 10 18 14 15 16 17 19 20 12 13 14 15 16 19 17 18 20 21 22 23 输出 #2 复制 2048 2048 2048 1024 512 512 提示 【样例1解释】 该样例即【题目描述】中所示的例子。 • 第一次操作后,叶子结点 4 和 6 上标有数字 1,叶子结点 5 和 7 上没有数字,因 此按任意 DFS 序拼成的序列均为 [1, 1],即所有的 23 = 8 个 DFS 序都是优的。 • 第二次操作后,叶子结点 4 ∼ 7 上分别标有数字 1, 2, 1, 2,因此共有 4 个优的DFS 序,分别为 [1, 4, 2, 3, 6, 5, 7],[1, 4, 2, 7, 3, 5, 6],[1, 2, 3, 6, 5, 7, 4],[1, 2, 7, 3, 5, 6, 4]。 c++
最新发布
09-12
<think>好的,用户需要查找洛谷P1173 NOI2016网格问题的Python解决方案或代码实现。首先,我需要回忆一下这个题目的具体要求。题目大意是判断一个网格是否会被障碍物分成不连通的部分,这可能涉及到图论中的割点或割边判断。 接下来,我需要考虑如何用Python实现这个问题的解法。这类题目通常需要高效的算法,因为数据规模可能较大。Python在处理大数据时可能会遇到性能问题,所以需要考虑优化方法。 首先,题目中的网格可以看作是一个图,其中某些格子被障碍物占据。判断是否存在割点,即移除某个点后图是否不连通,这是关键。Tarjan算法通常用于寻找割点,但实现起来需要注意细节,尤其是用Python时递归深度可能受限,可能需要用迭代方式或者优化代码。 然后,我需要查阅是否有现有的Python解法。不过用户提到站内引用中的内容似乎与快速排序或其他问题相关,可能不直接适用。但根据引用[2],用户提到了代码优化和UB(未定义行为)的问题,这提示在编写Python代码时需要注意避免类似错误,比如确保所有分支都有返回值,避免无限循环等。 另外,用户可能需要的是具体的代码示例。考虑到Python在二维网格问题中的处理,通常会用坐标转换、邻接表或矩阵来表示图结构。对于割点检测,可以使用Tarjan算法,维护每个节点的dfn(发现时间)和low(能回溯到的最早节点)值,判断是否存在子节点无法通过其他路径回到祖先节点。 不过,Python的递归深度限制可能对Tarjan算法产生影响,尤其是在大网格情况下。因此,可能需要用非递归的DFS实现,或者调整递归深度限制。但这也可能导致代码复杂度增加。 另外,题目中的障碍物可能形成多个连通块,需要处理多个情况。比如,当障碍物数量较少时,可能直接判断是否形成包围圈;当障碍物较多时,需要更复杂的判断逻辑。 总结步骤可能包括: 1. 将网格转换为图结构,标记障碍物。 2. 使用Tarjan算法寻找割点。 3. 判断是否存在割点导致图不连通。 4. 处理特殊情况,如网格过小、障碍物数量不足等。 在编写Python代码时,需要注意数据结构的效率,比如使用集合或字典来快速访问节点,避免重复计算。此外,可能的优化包括剪枝、预处理等。 最后,根据用户提供的引用,虽然引用内容不直接相关,但需要注意代码的正确性和优化,避免出现未定义行为或逻辑错误。例如,确保所有函数都有正确的返回语句,避免因编译器优化导致的错误,这在Python中可能表现为逻辑错误而非运行错误。</think>关于洛谷 P1173 NOI2016 网格问题的 Python 解法,该问题属于图论中的连通性判断问题,需要判断是否存在障碍物形成的割点导致网格不连通。以下是解题思路和实现要点: --- ### **问题核心分析** 题目要求判断 $n \times m$ 网格中,给定 $c$ 个障碍物的位置后,是否会将网格分割为多个不连通区域。关键在于判断是否存在 **割点**(即移除该点会导致图不连通)或 **割边**。 --- ### **算法选择** 1. **Tarjan 算法**:用于寻找无向图的割点。若存在割点,则网格可能被分割。 2. **连通性检测**:需考虑障碍物分布对整体连通性的影响,特别是边界情况和稀疏障碍物场景[^1]。 --- ### **Python 实现步骤** #### 1. **坐标离散化与邻接表构建** 将网格坐标转换为图中的节点,并为每个非障碍物节点建立邻接关系: ```python def build_graph(n, m, obstacles): obstacles_set = set(obstacles) graph = defaultdict(list) directions = [(-1,0), (1,0), (0,-1), (0,1)] for x in range(n): for y in range(m): if (x, y) not in obstacles_set: for dx, dy in directions: nx, ny = x + dx, y + dy if 0 <= nx < n and 0 <= ny < m and (nx, ny) not in obstacles_set: graph[(x, y)].append((nx, ny)) return graph ``` #### 2. **Tarjan 算法实现割点检测** 通过深度优先搜索(DFS)计算每个节点的 `dfn`(发现时间)和 `low`(回溯值): ```python def find_cut_points(graph, start): visited = set() dfn, low = {}, {} parent = {} cut_points = set() time = 0 def tarjan(u): nonlocal time dfn[u] = low[u] = time time += 1 children = 0 for v in graph[u]: if v not in dfn: parent[v] = u children += 1 tarjan(v) low[u] = min(low[u], low[v]) if parent.get(u) is None and children > 1: cut_points.add(u) elif parent.get(u) is not None and low[v] >= dfn[u]: cut_points.add(u) elif v != parent.get(u): low[u] = min(low[u], dfn[v]) tarjan(start) return cut_points ``` #### 3. **特殊情况处理** - **极小网格**:如 $n \times m \leq 2$ 时无需判断。 - **障碍物数量不足**:若障碍物数量少于 2,则无法分割网格[^2]。 --- ### **完整代码框架** ```python from collections import defaultdict def main(): n, m, c = map(int, input().split()) obstacles = [tuple(map(int, input().split())) for _ in range(c)] if n * m - c < 2: print("No") return if c == 0: print("Yes" if n * m >= 2 else "No") return graph = build_graph(n, m, obstacles) nodes = list(graph.keys()) if not nodes: print("No") return cut_points = find_cut_points(graph, nodes[0]) print("Yes" if len(cut_points) > 0 else "No") if __name__ == "__main__": main() ``` --- ### **注意事项** 1. **性能优化**:Python 递归深度有限,对大规模网格需改用迭代式 DFS。 2. **边界条件**:注意坐标范围和障碍物去重。 3. **连通性验证**:需确保所有非障碍物节点均被访问,防止漏判。 --- 相关问题
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值