CodeForces 616E(数学规律)

本文介绍了一种高效的算法,用于解决大规模数值n和m下的模运算求和问题n%1+n%2+...+n%m。通过巧妙地将原问题转化为求等差数列之和,实现了对复杂度的有效降低。

题意:

输入n, m(1  ≤  n, m  ≤  10^13),求 n%1 + n%2 + ... + n%m的值.

思路:

n%i = n - n/i(整除)*i;

所以 ∑(i=1, m) n%i 可以转化为 m*n - ∑(i=1, m) n/i*i;

易知:给定一个i,被n整除得c = n/i,另r = n/c,很容易可以得到n整除i到r范围内的所有数的值都是相同的,所以我们另i为下界,r为上界。那么我们求 ∑(i=1, m) n/i*i的时候就可以将它们分成若干组进行求和,另(n/i)相同的连续一段为一组。所以就可以利用等差数列求和公式对i到r范围内进行求和然后再乘上(n/i)就得到了这一段的ans,同理,其他段也是如此。


#include <algorithm>
#include <iostream>
#define LL long long
using namespace std;
const LL mod = 1e9+7;
LL n, m, ans, up, sum, r;
int main()
{
	cin >> n >> m;
	ans = (n%mod)*(m%mod)%mod;
	up = min(n, m);	//当n>m时, 只能求m个模; n<m时, 只能求n个模 
	sum = 0;
	for(LL i = 1; i <= up; ++i)
	{
		r = min(n/(n/i), up);//如果上界超过up, 缩至up 
		LL a = i+r;	//准备求和公式 
		LL b = r-i+1;
		if(a&1) b /= 2;
		else a /= 2;
		sum = (sum + ((a%mod)*(b%mod)%mod)*(n/i)%mod)%mod;//获得此段的ans 
		i = r;
	}
	cout << (ans-sum+mod)%mod << endl;//防止mod之后相减变负数 
	return 0;
}


继续加油~
你提供的 C 代码实现了一个根据输入 `k` 输出对应结果的程序。我们来分析它的逻辑,并解释其行为,然后给出优化建议和相关问题。 --- ### ✅ 功能分析 该程序读取一个测试用例数量 `t`,对每个测试用例读取一个整数 `k`,然后输出一个与 `k` 相关的结果。 我们观察输出规律: - 当 `k == 1` → 输出 `3` - 当 `k == 2` → 输出 `5` 对于 `k >= 3`,程序将 `k` 分为: - `e = k / 3`(整除) - `j = k % 3`(余数) 然后根据余数分类处理: | 余数 j | 输出值 | |--------|--------------------------| | 0 | `(e + 1) * 4 - 1 = 4*(e+1) - 1` | | 1 | `4*(e + 1)` | | 2 | `4*(e + 1)` | 我们尝试列出前几项看看是否有模式: | k | e = k//3 | j = k%3 | 输出 | |---|---------|--------|------------| | 1 | 0 | 1 | 3 (特判) | | 2 | 0 | 2 | 5 (特判) | | 3 | 1 | 0 | 4*(1+1)-1 = 7 | | 4 | 1 | 1 | 4*(1+1) = 8 | | 5 | 1 | 2 | 8 | | 6 | 2 | 0 | 4*3 -1 = 11 | | 7 | 2 | 1 | 12 | | 8 | 2 | 2 | 12 | | 9 | 3 | 0 | 15 | |10 | 3 | 1 | 16 | 所以序列是: `k: 1 2 3 4 5 6 7 8 9 10 ...` `f(k): 3, 5, 7, 8, 8, 11, 12, 12, 15, 16, ...` 我们可以发现: - 从 `k=3` 开始,每 **3 个数**一组,输出呈规律: - 第一组(k=3,4,5)→ 7, 8, 8 - 第二组(k=6,7,8)→ 11, 12, 12 - 第三组(k=9,10,11)→ 15, 16, 16 每组首项是 `4*(e+1)-1`,后两项是 `4*(e+1)`,其中 `e = k // 3` 但注意:`k=1` 和 `k=2` 是例外。 --- ### 🔍 重新审视逻辑:是否可以统一? 我们注意到: - `k=1` → 3 - `k=2` → 5 如果我们也尝试套用公式(假设从 k=1 起分组): | k | e = (k-1)//3 | 组内偏移 | 想要输出 | |---|--------------|----------|---------| | 1 | 0 | 0 | 3 | | 2 | 0 | 1 | 5 | | 3 | 0 | 2 | 7 | | 4 | 1 | 0 | 8 | | 5 | 1 | 1 | 8 | | 6 | 1 | 2 | 11 | 不太一致。 另一种思路:这个数列可能是在跳过某些数字(比如不是 4 的倍数?),或者构造某个特殊集合。 但我们更关心的是:**这段代码到底在解决什么问题?** 从输出来看,这可能是某道 OJ 题(如 Codeforces、LeetCode 类型题)的答案程序,目标是求第 `k` 个不属于某个集合的正整数,或某种构造序列。 然而当前代码存在几个问题: --- ### ❌ 存在的问题 #### 1. `k==2` 输出 `5`,但没有明显延续性 如果我们相信公式从 `k>=3` 成立,那 `k=1` 和 `k=2` 是特例。 但为什么 `k=2` 输出 `5`?而 `k=3` 输出 `7`,中间跳过了 `6`? 或许这个序列是:**不包含偶数?不对,有 8,12 等。** 再看输出序列:3,5,7,8,8,11,12,12,15,16,16,... 等等!第4和第5个都是8?不可能! ⚠️ **这里出现严重错误:同一个 `k` 对应唯一输出,但 `k=4` 和 `k=5` 都输出 `8`,说明这不是一一映射,而是允许重复吗?这不合常理。** 除非题目是“构造一个非递减序列”,但通常不会两个不同 `k` 映射到相同值还合理。 但是看你的代码: ```c if(j==1){ printf("%lld\n",(e+1)*4); } else if(j==2){ printf("%lld\n",(e+1)*4); } ``` 👉 `j==1` 和 `j==2` 都输出相同的值! 也就是说,对于每组三个 `k` 值(如 k=4,5,6),只有前两个共享输出? 但上面表格中: - k=4 → j=1 → 8 - k=5 → j=2 → 8 ✅ - k=6 → j=0 → 11 所以实际上: - k=4,5 → 输出 8 - k=6 → 输出 11 但这意味着:**第4个和第5个答案一样?** 除非题目允许这样(例如某种分段函数),否则逻辑有问题。 --- ### ✅ 更合理的解释(推测原题) 这很像是一道构造“去掉所有形如 `4n+2` 的数”后的第 `k` 小正整数,或者其他类似构造。 但让我们反向思考:是否存在一个已知序列满足这种模式? 观察: 我们定义 `f(k)`: | k | f(k) | |--|-----| |1|3| |2|5| |3|7| |4|8| |5|8| ← 重复了! 重复不合理。说明代码可能错了。 --- ### 🛠️ 错误定位 最大的问题是:**k=4 和 k=5 输出相同值 `8`**,这是不可接受的,除非题目明确允许。 但更有可能的是:**你想表达的是每组三个 k 对应三个不同的规则,但 j==1 和 j==2 却用了相同的输出。** 应该是笔误! 我们回顾一下常见套路:比如构造一个序列,排除掉某些数(比如 4 的倍数减 1?) 换个角度:假设我们想要构造如下序列: 保留所有正整数,但删除所有形如 `4n - 1` 的数(即 3,7,11,15,...),但显然又不是,因为 3 和 7 都出现了。 另一个可能性:**构造一个序列,使得每三个数中分别对应三种情况。** 比如,按如下方式生成序列: - 每组三个数:A, B, B - 数值为:4n-1, 4n, 4n 例如: - n=1: 3,4,4 - n=2: 7,8,8 - n=3: 11,12,12 - n=4: 15,16,16 那么序列就是:3,4,4,7,8,8,11,12,12,15,16,16,... 现在看 `k=1` → 3 ✅ `k=2` → 4 ❌ 但你输出是 5 不匹配。 但如果起始偏移不同呢? 也许这个序列是:**跳过所有的 `4n+2` 形式的数?** 即:不允许出现 2,6,10,14,... 自然数中去掉这些: 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,... 去掉 2,6,10,14 → 得到: 1,3,4,5,7,8,9,11,12,13,15,16,... 第 k 项: - k=1 → 1 - k=2 → 3 - k=3 → 4 - k=4 → 5 - k=5 → 7 - k=6 → 8 - k=7 → 9 - k=8 → 11 但你的程序: - k=1 → 3 - k=2 → 5 - k=3 → 7 不符。 --- ### ✅ 正确理解:可能是「第 k 个不能被表示为 x^2 - y^2 的正整数」类问题 但最简单的结论是: > 这段代码试图实现一个分组映射,每组 3 个 k 值映射到近似线性的输出,但在 `j==1` 和 `j==2` 时输出相同,导致多个 `k` 对应同一结果,这很可能是个 bug。 --- ### ✅ 修改建议(修复重复输出问题) 如果你本意是让: - j == 0 → 4*(e+1) - 1 - j == 1 → 4*(e+1) - j == 2 → 4*(e+1) + 1 那就不会重复。 或者如果你希望每组输出三个连续数,或其他。 但现在你写成: ```c if(j==1){ printf("%lld\n",(e+1)*4); }else if(j==2){ printf("%lld\n",(e+1)*4); // 和上面一样! } ``` 这就是问题所在。 --- ### ✅ 推测正确逻辑(修正版) 假设正确的逻辑是: - k=1 → 3 - k=2 → 5 - 然后从 k=3 开始进入循环模式,每组三个 k 对应三个输出:`4n-1`, `4n`, `4n` 即: - k=3 → 7 (4×2-1) - k=4 → 8 (4×2) - k=5 → 8 (重复) - k=6 → 11(4×3-1) - k=7 → 12 - k=8 → 12 这就符合你现在的输出。 所以这个序列是:**每三个位置输出:(4n-1), 4n, 4n** 即:对 `k>=3`,令 `e = (k-1)/3`,`j = (k-1)%3` - 若 j=0 → 4*(e+1)+3? 不好算。 不如保持原索引方式: ```c long long e = k / 3; // 整除 long long j = k % 3; ``` 当 k=3 → e=1, j=0 → 输出 4*(1+1)-1 = 7 k=4 → e=1, j=1 → 8 k=5 → e=1, j=2 → 8 k=6 → e=2, j=0 → 11 所以确实是设计如此:**允许两个 k 共享一个值**。 并且 k=1 和 k=2 是特例。 所以最终结论: ✅ 你的代码逻辑可能是正确的(如果题目确实要求如此),但它缺乏注释,且 `k=2` 输出 `5` 没有遵循后续模式,需要确认是否合理。 --- ### ✅ 改进建议(清晰化代码) ```c #include <stdio.h> int main() { long long t; scanf("%lld", &t); while (t--) { long long k; scanf("%lld", &k); if (k == 1) { printf("3\n"); } else if (k == 2) { printf("5\n"); } else { long long group = k / 3; // 商 long long remainder = k % 3; // 余数 if (remainder == 0) { // 第3、6、9...个 → 输出 4*(group+1) - 1 printf("%lld\n", 4LL * (group + 1) - 1); } else if (remainder == 1 || remainder == 2) { // 第4&5、7&8、10&11... → 输出 4*(group+1) printf("%lld\n", 4LL * (group + 1)); } } } return 0; } ``` ### ✅ 解释 - 该程序处理一个序列,其中: - 第1项是 3 - 第2项是 5 - 从第3项开始,以3为周期分组: - 每组第一个(k%3==0)输出 `4*(e+1)-1` - 后两个(k%3==1 或 2)输出 `4*(e+1)` - 例如: - k=3 → 4*(1+1)-1 = 7 - k=4 → 4*(1+1) = 8 - k=5 → 8 - k=6 → 4*(2+1)-1 = 11 - k=7 → 12 - k=8 → 12 虽然有重复值,但如果题目允许,则逻辑成立。 --- ### ❓ 你需要回答的问题: 1. 这个程序解决的实际问题是什么?(来自哪道题?) 2. 是否允许不同 `k` 对应相同输出? 3. 为什么 `k=2` 输出 `5`?它和其他项有什么关系? 4. 是否可以用数学公式直接计算 `f(k)` 而不需要判断? --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值