k倍区间 前缀和【蓝桥杯2017 C/C++ B组】

标题: k倍区间

给定一个长度为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

资源约定:
峰值内存消耗(含虚拟机) < 256M
CPU消耗  < 2000ms

先求出每一位的前缀和并取余   那么任意区间[l,r]的和就可以通过sum[r]-sum[l-1]来得到

要求区间和为k的倍数,即(sum[r]%k - sum[l-1])%k==0

移项 sum[r] % k == sum[l-1] % k

那么每两个对k取余得到值相同的sum 就可以组成一个k倍区间

所以我们要统计下余数为0--k-1的前缀和个数

取余为0的前缀和 说明[0,i]区间就已经存在一个k倍区间了   当然他还能跟另一个前缀和取余为0的[0,j]区间 组合成为[i,j]的k倍区间

所以刚开始我们应该先把取余为0的个数加上

然后对于所有取余相同的  两两配对能组成一个k倍区间,那么假设有x个取余相同的前缀和 那么他可以组成 ((x-1)*x)/2 个k倍区间     例如有 4个数 两两组合 3+2+1 = 3*(3-1)=6   

其实是等差数列求和公式   n*(n+1)/2    不过这里的n = x-1; 

#include<bits/stdc++.h>
using namespace std;
//int 只有85分 
long long a[100005],sum[100005],num[100005];
int main()
{
    int n,k ;
    cin >> n >> k;
    for(int i = 0; i < n; i++){
      cin  >> a[i];
    }
    sum[0] = a[0]%k;
    for(int i = 1; i < n; i++){
      sum[i] = (sum[i-1]+a[i])%k; //求每个前缀和并趋于
    }
    memset(num,0,sizeof num);
    for(int i = 0; i < n; i++){
      num[sum[i]] ++;   // 统计余数为 0 -- k-1的个数
    }
    long long ans = num[0]; //能取余为0 自己本身就存在一个k倍区间了
    for(int i = 0; i < k; i++){
      ans += (num[i]*(num[i]-1))/2 ; //两两配对 等差数列 n*(n+1)/2   这里n=num[i]-1
    }
    cout << ans << endl;
}
原文地址:https://www.cnblogs.com/llke/p/10780138.html