洛谷P2261 [CQOI2007]余数求和

本文介绍了一种求解特定数学问题的高效算法,通过枚举优化的方式将时间复杂度降低到$O(\sqrt{n}

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

传送门

 

题目要求$\sum_{i=1}^n k\%i$

那么就是$\sum_{i=1}^n k\%i=\sum_{i=1}^n k-i*\left\lfloor\frac{k}{i}\right\rfloor=n*k-\sum_{i=1}^n i*\left\lfloor\frac{k}{i}\right\rfloor$

那么我们可以枚举$\left\lfloor\frac{k}{i}\right\rfloor$的取值,因为个数只有$\sqrt{n}$,所以时间复杂度是$O(\sqrt{n})$

代码短小精炼

 1 //minamoto
 2 #include<cstdio>
 3 #define ll long long
 4 #define min(a,b) ((a)<(b)?(a):(b))
 5 int main(){
 6     ll n,k;scanf("%lld%lld",&n,&k);
 7     ll ans=n*k;
 8     for(ll l=1,r;l<=n;l=r+1){
 9         r=(k/l)?min(k/(k/l),n):n;
10         ans-=k/l*(r-l+1)*(l+r)/2;
11     }
12     printf("%lld\n",ans);
13     return 0;
14 }

 

转载于:https://www.cnblogs.com/bztMinamoto/p/9598263.html

<think>首先,用户查询的是洛谷平台编号为P8649的问题详情。我需要从提供的引用中提取相关信息。 回顾引用: - 引用[2]提到:【洛谷】P8649 [蓝桥杯 2017 省 B] k 倍区间 题解。给定一个长度为 N 的数列,A1 到 AN,如果一段连续子序列 Ai 到 Aj (i≤j) 之和是 K 的倍数,则称 [i,j] 是 K 倍区间。 - 引用[3]详细描述了解决方案:使用前缀和数组 s[i],计算前缀和对 K 取模的结果,并统计不同余数出现的次数。然后,通过组合数学计算 K 倍区间的数量。 - 引用[4]展示了 TLE 代码(28分),使用双重循环计算区间和,效率低。 - 引用[5]提到 Java 题解,时间复杂度 O(n),空间复杂度 O(k)。 所以,问题 P8649 是关于“k 倍区间”的,要求计算给定序列中,有多少个连续子序列的和是 K 的倍数。 关键点: - 输入:一个长度为 N 的数列,以及整数 K。 - 输出:K 倍区间的个数。 - 高效解法:使用前缀和和模运算,避免 O(n^2) 的暴力方法。 我需要以中文回答,结构清晰,逐步解决问题。回答中要包含必要的数学表达式,使用 LaTeX 格式: - 行内数学表达式用 $...$,如 $s[i] = s[i-1] + a[i]$。 - 独立公式用 $$...$$,如公式块。 回答结构: 1. **问题描述**:简要介绍问题内容。 2. **输入输出格式**:说明输入和输出要求。 3. **解法思路**:详细解释高效解法,包括前缀和、模运算和组合数学。 4. **代码实现**:提供示例代码(基于引用[3]的 C++ 代码)。 5. **复杂度分析**:讨论时间和空间复杂度。 6. **相关问题生成**:在最后添加相关问题。 引用标识:在回答中引用段落末尾添加 [^数字],如 [^2]。 最后,生成相关问题,如示例所示。 完整回答: - 开头:直接回答问题。 - 中间:逐步解释。 - 结尾:相关问题。 相关问题示例: 1. 如何优化前缀和算法? 2. 同余定理在算法中的应用? 3. 类似问题(如子数组和问题)的解法? 现在,构建回答。</think>### 洛谷 P8649 [蓝桥杯 2017 省 B] k 倍区间 问题详情 #### 问题描述 洛谷 P8649 是一道算法题,要求计算给定一个长度为 $N$ 的整数序列 $A_1, A_2, \cdots, A_N$ 中,有多少个连续子序列(区间 $[i,j]$ 满足 $i \leq j$)的和是整数 $K$ 的倍数。这样的区间被称为“$K$ 倍区间”。例如,序列 $[1, 2, 3]$ 和 $K=3$ 时,区间 $[2,3]$ 的和为 $2+3=5$(不是 3 的倍数),而区间 $[1,3]$ 的和为 $1+2+3=6$(是 3 的倍数)[^2]。 #### 输入输出格式 - **输入格式**: - 第一行包含两个整数 $N$ 和 $K$($1 \leq N \leq 10^5$,$1 \leq K \leq 10^5$)。 - 第二行包含 $N$ 个整数 $A_1, A_2, \cdots, A_N$(每个整数绝对值不超过 $10^9$)。 - **输出格式**: - 输出一个整数,表示 $K$ 倍区间的个数。 #### 解法思路(高效方法) 暴力解法(双重循环计算每个区间和)的时间复杂度为 $O(N^2)$,在 $N$ 较大时会超时(如引用[4]的代码仅得 28 分)。高效解法基于前缀和和同余定理,时间复杂度为 $O(N)$,空间复杂度为 $O(K)$。步骤如下: 1. **前缀和定义**: 定义前缀和数组 $s$,其中 $s[i] = \sum_{k=1}^{i} A_k$($s[0] = 0$)。则区间 $[i,j]$ 的和可表示为 $s[j] - s[i-1]$。 2. **同余定理应用**: 区间 $[i,j]$ 是 $K$ 倍区间当且仅当 $(s[j] - s[i-1]) \equiv 0 \pmod{K}$。根据同余定理,这等价于 $s[j] \equiv s[i-1] \pmod{K}$[^3]。因此,问题转化为:统计前缀和数组 $s$ 中,有多少对索引 $(i,j)$($i < j$)满足 $s[i] \equiv s[j] \pmod{K}$。 3. **余数统计与组合计算**: - 使用数组 $b$(或哈希表)记录每个余数 $r$($0 \leq r < K$)出现的次数。初始化 $b[0] = 1$(因为 $s[0] = 0$ 自身可构成一个区间)。 - 遍历前缀和数组,对每个 $s[i] \mod K$,更新 $b[r] \gets b[r] + 1$。 - 最终,$K$ 倍区间的总数由组合公式计算: $$ \text{ans} = \sum_{r=0}^{K-1} \binom{b[r]}{2} = \sum_{r=0}^{K-1} \frac{b[r] \times (b[r] - 1)}{2} $$ 这里 $\binom{b[r]}{2}$ 表示从相同余数的位置中任选两个的组合数(对应一个有效区间)[^3][^5]。 4. **处理负数余数**: 在代码实现中,需确保余数为非负数。可通过 $(s[i] \mod K + K) \mod K$ 调整[^5]。 #### 代码实现(C++ 示例) 以下是基于引用[3]的高效 AC 代码(时间复杂度 $O(N)$,空间复杂度 $O(K)$): ```cpp #include <iostream> using namespace std; int main() { long long n, k, s[100005] = {0}, b[100005] = {0}; // b 数组记录余数出现次数 long long ans = 0; cin >> n >> k; b[0] = 1; // 初始化 s[0] = 0 的余数 for (int i = 1; i <= n; i++) { int t; cin >> t; s[i] = s[i - 1] + t; // 计算前缀和 long long r = (s[i] % k + k) % k; // 确保余数非负 b[r]++; // 更新余数计数 } for (int i = 0; i < k; i++) { ans += b[i] * (b[i] - 1) / 2; // 组合公式计算 } cout << ans << endl; return 0; } ``` #### 复杂度分析 - **时间复杂度**:$O(N + K)$。遍历序列一次计算前缀和($O(N)$),再遍历余数数组($O(K)$)。由于 $K \leq 10^5$,整体高效[^5]。 - **空间复杂度**:$O(K)$。用于存储余数计数数组 $b$[^3][^5]。 #### 注意事项 - 数据范围:$N$ 和 $K$ 最大为 $10^5$,需使用 `long long` 避免溢出。 - 边界情况:当 $K=1$ 时,所有区间都满足条件,但组合公式仍正确。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值