蓝桥杯2017年第八届真题-k倍区间-题解(C++代码)

博客介绍了如何高效解决寻找数列中连续子序列和为K倍数的区间问题。通过使用前缀和与动态规划的方法,将时间复杂度降低到线性级别,避免了暴力求解导致的超时。代码示例展示了C++实现,通过对每个数计算其对K取模的余数,并更新对应余数的计数,得到最终的K倍区间数量。

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

题目描述:

给定一个长度为N的数列,A1, A2, ... AN,如果其中一段连续的子序列Ai, Ai+1, ... Aj(i <= j)之和是K的倍数,我们就称这个区间[i, j]是K倍区间。
你能求出数列中总共有多少个K倍区间吗?

输入格式:
第一行包含两个整数N和K。(1 <= N, K <= 100000)
以下N行每行包含一个整数Ai。(1 <= Ai <= 100000)

输出格式:

输出一个整数,代表K倍区间的数目。

样例输入:

5 2
1
2
3
4
5

样例输出:

6

资源限制

内存限制:256.0MB C/C++时间限制:1.0s

原题链接:“蓝桥杯”练习系统

解题思路:前缀和+动态规划

最简单的方法是纯暴力求解,枚举所有的子序列,但是这样做复杂度为O(n^2),会超时,无法通过所有数据。因此,我们可以对其进行优化,用S[n]存下前n个数的前缀和,当(Sr - Sl) % k == 0时,则区间[l+1...r]是K倍区间,但是用了前缀和后直接去计算,也会超时,只是减少了一部分计算量,复杂度还是O(n^2)。因此,我们还可以进行优化,我们发现(Sr - Sl) % k = 0可以转化成Sr % k = Sl  % k,也就是说当Sr和Sl模k的余数相等时,区间[l+1...r]是K倍区间。因此我们可以创建一个数组remainder来保存每个Si模k的余数,对每个Si,前面(S0....Si - 1)与其模k余数相等的个数即为以第i个数结尾的子串中(相当于计算(Si - S0) % k == 0 ..... (Si - S(i - 1)) % k == 0),K倍区间的个数,把每个Si的K倍区间的个数相加即为最后的结果,由于不需要计算Sr - Sl了,因此不必存储Si,只需要在每次输入一个数后计算当前Si即可。

代码如下(C++):

#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

int main(){
	int n,k;
	cin>>n>>k;
	long long sum = 0,count = 0;
	int remainder[k];	//定义一个数组,remainder[i]表示Sn % k == i的个数 
	memset(remainder,0,sizeof remainder);
	remainder[0]=1;	//S0 = 0,0 % k = 0,因此S0模k的余数为0, remainder[0]初值为1 
	while(n--){
		int num;
		cin>>num;		
		sum += num;	//计算当前Sn 
		
		count += remainder[sum % k];	//加上与当前Sn对k取模结果相同的Si的个数,即为以第i个数结尾的子串中,K倍区间的个数 
		
		remainder[sum % k]++;	//当前Sn % k == i, remainder[i]个数加1
		
	}
	cout<<count<<endl;
	return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值