k倍区间(从暴力破解到利用两种数学思维的过程)

题目描述

输入描述

输出描述

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

输入输出样例

示例

输入

5 2
1
2
3
4
5

输出

6

运行限制

  • 最大运行时间:2s
  • 最大运行内存: 256M

想法一:暴力求解

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n,k;
	cin >> n >> k;
	int *a = new int[n];
	int sum = 0;
	for(int i=0;i<n;i++){
		cin >> a[i];
	}
	for(int i=0;i<n;i++){
		int b = a[i];
		if(a[i]%k == 0){
			++sum;
		}
		for(int j = i+1;j<n;j++){
			b += a[j];
			if(b%k == 0){
				++sum;
			}
		}
	}
	cout << sum;
	
	return 0;
}
  • 只能通过两个样例,其余均超时,所以要考虑用空间换取时间。

想法二:前缀和

  • 这是我第一次接触,类似于动态规划

  • 前缀和是什么?
    用一个简单的列子去介绍

    原数组: a[1], a[2], a[3], a[4], a[5], …, a[n]
    前缀和: s[i] = a[1] + a[2] + a[3] + … + a[i]
    前缀和就是用一个数组s去存数组a的前n项的和。
    s[0] = 0
    s[1] = a[1]
    s[2] = a[1] + a[2]
    s[n] = a[i] + a[2] + a[3] + …+a[n]
    这样s[n]对应的就是a[1]—a[n]的和,s的每一项都这样对应,就构成了前缀和。
    注:前缀和的下标一定要从1开始。
    ————————————————
    版权声明:本文为CSDN博主「HaiFan.」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/weixin_73888239/article/details/128313429
    
  • 前缀和求区间大小

    如何利用前缀和去求区间大小呢?
    有一个公式:s[r] - s[l - 1]。
    就是这个公式,他的时间复杂度O(1),这就要比暴力的做法快上很多了。
    
  • 如何构成前缀和的形式?

    	for (int i = 1; i <= n; i++)
    	{
    		s[i] = s[i - 1] + a[i];
    	}
    
    s[1] = s[0] + a[1];
    s[2] = s[1] + a[2];
    s[3] = s[2] + a[3];
    s[n] = s[n - 1] + a[n]
    去遍历a数组,把当前a[n]的数加上s[n-1]的数,就能得到s[n],这个s[n]就是a[1,n]的和。
    
  • 代码实现

#include<bits/stdc++.h>
using namespace std;
int a[100010];
int s[100010];
int main(){
	int n,k;
	cin >> n >> k;
	s[0] = 0;
	for(int i=1;i<=n;i++){
			cin >> a[i];
			s[i] = s[i-1] + a[i];
		}
	long long ans = 0;
	for(int i=1;i<=n;i++){
		for(int j=i;j<=n;j++){
			if((s[j]-s[i-1])%k == 0){
				++ans;
			}
		}
	}
	cout << ans << endl;
	
	return 0;
}

想法三:前缀和+差分

  • 想法一和二没什么区别,时间复杂度相同。
#include<bits/stdc++.h>
using namespace std;
long a[100010];
long s[100010];
int main(){
	int n,k;
	cin >> n >> k;
  
	for(int i=1;i<=n;i++){
				cin >> a[i];
				a[i]+=a[i-1];
				s[a[i]%k]++;
				
			}
		long long ans = s[0];
		for(int i = 0;i < k;++i){
			ans += (s[i]*(s[i]-1))/2;
		}
		cout << ans << endl;
	
	return 0;
}
  • 先求出前i项和中属于k倍区间的数,加到s[0]中
  • 其余不是k的倍数的区间,余数相同的两个区间相减得到的中间区间为k的倍数,将所有余数相同的区间两两相减,所得到的组合数为k的区间数
  • 二者相减即可得出结果
posted @ 2023-03-09 11:23  ku然  阅读(56)  评论(0编辑  收藏  举报