每日一题之k倍区间

题目描述

给定一个长度为 N 的数列,A1,A2,⋯AN,如果其中一段连续的子序列 Ai,Ai+1,⋯Aj ( i≤j ) 之和是 K 的倍数,我们就称这个区间 [i,j] 是 K 倍区间。

你能求出数列中总共有多少个 K 倍区间吗?

输入描述

第一行包含两个整数 N 和 K( 1≤N,K≤105 )。

以下 N 行每行包含一个整数 Ai​ ( 1≤Ai​≤105 )

输出描述

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

#include 
using namespace std;
int main()
{
  // 请在此输入您的代码
  long long n,k;
  cin>>n>>k;
  long long a[n],cnt=0;
  for(long long i=0;i>a[i];
  }
  long long sum=0;
  for(long long i=0;i

这是最暴力的做法,但是只能通过两个测试点,因为它的时间复杂度为O(n²)。但是它的思路非常简单,就是求出所有子序列的和,然后看它能不能整除k。

现在介绍一种我觉得很难想到的办法,首先有一个结论:如果两个前缀和的余数相同,则它们之间的子序列和一定能被 k 整除。简单的证明如下:

  1. 前缀和定义

    • 前缀和 prefixSum[i] 表示从 a[0] 到 a[i] 的和:

      prefixSum[i]=a[0]+a[1]+⋯+a[i]
    • 前缀和 prefixSum[j]表示从 a[0] 到 a[j] 的和:

      prefixSum[j]=a[0]+a[1]+⋯+a[j]
  2. 子序列和

    • 从 i+1 到 j 的子序列和为:

      sum(i+1,j)=a[i+1]+a[i+2]+⋯+a[j]
    • 根据前缀和的定义,可以表示为:

      sum(i+1,j)=prefixSum[j]−prefixSum[i]
  3. 余数相同

    • 假设 prefixSum[i]mod  k=r,即:

      prefixSum[i]=k⋅q1+r

      其中 q1 是某个整数。

    • 同理,假设 prefixSum[j]mod  k=r,即:

      prefixSum[j]=k⋅q2+r

      其中 q2​ 是某个整数。

  4. 子序列和对 k 取模

    • 将 sum(i+1,j)=prefixSum[j]−prefixSum[i] 代入:

      sum(i+1,j)=(k⋅q2+r)−(k⋅q1+r)=k⋅(q2−q1)
    • 显然,sum(i+1,j) 是 k 的倍数,即:

      sum(i+1,j)mod  k=0

接下来我们举个例子:1 2 3 4 5,k=2

1和1 2对2的余数相同,那么它们的子序列和2一定能被2整除,1 2 3和1 2 3 4对2的余数相同,那么他们的子序列和4一定能被2整除,1 2和1 2 3 4 5对2的余数相同,那么它们的子序列和3+4+5=12一定能被2整除,1和1 2 3 4 5同样可得2+3+4+5=14能被2整除。至此,加上1 2 3和1 2 3 4这两个序列,总共6个能被2整除的序列已经全部找到。

#include 
#include 
using namespace std;

int main() {
    long long n, k;
    cin >> n >> k;
    long long a[n];
    for (long long i = 0; i < n; i++) {
        cin >> a[i];
    }

    unordered_map modMap;
    modMap[0] = 1; // 初始状态,前缀和为 0 的情况
    long long prefixSum = 0, cnt = 0;

    for (long long i = 0; i < n; i++) {
        prefixSum += a[i];
        long long mod = (prefixSum % k + k) % k; // 处理负数情况
        if (modMap.find(mod) != modMap.end()) {
            cnt += modMap[mod];
            modMap[mod]++;
        } else {
            modMap[mod] = 1;
        }
    }

    cout << cnt;
    return 0;
}

这里需要注意的是modMap[0]=1,当有一个序列和的余数为0时,说明该序列和能被k整除,而当有第二个序列和的余数为0时,那么他们的子序列也能被k整除。

你可能感兴趣的:(算法,数据结构)