连续自然数之和

连续自然数之和

目录

目录

连续自然数之和

目录

题目描述

输入

输出

样例输入

样例输出

题目解读

解析

*证明(可跳过)

代码


题目描述

有n个正整数排成一行。你的目的是要从中取出一个或连续的若干个数,使它们的和能够被k整除。 例如,有6个正整数,它们依次为1、2、6、3、7、4。若k=3,则你可以取出1、2、6,或者2、6、3、7,也可以仅仅取出一个6或者3使你所取的数之和能被3整除。当然,满足要求的取法不止以上这4种。事实上,一共有7种取法满足要求。 给定n和k,以及这n个数。你的任务就是确定,从这n个数中取出其中一个数或者若干连续的数使它们的和能被k整除有多少方法。 由于取法可能很多,因此你只需要输出它mod 1234567的值即可。

输入

第一行有两个正整数,分别代表n和k。输入数据保证有n小于等于500 000,k小于等于100 000。 以下n行每行一个正整数。这些正整数保证都不大于10 000。

输出

一个正整数。它应该是你的答案mod 1234567的结果。

样例输入

6 3
1
2
6
3
7
4

样例输出

7

题目解读

首先我们先将一些重要的地方提取出来

  • 你的目的是要从中取出一个连续的若干个数,使它们的能够被k整除
  • 由于取法可能很多,因此你只需要输出它mod 1234567的值即可。
  • n小于等于500 000,k小于等于100 000(这两个截然不同,注意!!!)

由第一点我们很容易想到前缀和表示数(F[ai + 1] + F[ai + 2] …… F[aj] = sum[j] - sum[i]

解析

我们设 A = sum[J] - sum[I],B = sum[Q] - sum[F]

则我们可得 A % k = R,B % k = R,则此时 k | (A - B)

*证明(可跳过)

 设 A = sum[J] - sum[I] = ka + r,B = sum[Q] - sum[F] = kb + r

则 A - B = ka + r - (kb + r) = k(a - b),所以此时 k | k(a - b),即 k | (A - B)

因而我们可以用一个数组存R存 sum % k 的余数个数,不明白的话可以看代码理解

接着我们用组合数学的知识求解:

因为每两个数相减可得一组答案,而R数组又替我们存了有相同余数的总个数,所以我们可以用C的公式求解

值得注意的是:当余数为 0 时,还需加上R数组存的个数,因为他们不需要相减也是 K 的倍数

代码

#include<cstdio>
#define M 100000
#define mod 1234567
#define LL long long
#define reg register
 
int n;
LL sum,R[M + 5];
LL ans,k;
 
LL C(LL N,LL m){
    LL Ans = 1;
    for (reg int i = 1;i <= m; ++ i)
        Ans = Ans * (N - m + i) / i;
    return Ans;
}
 
int main(){
    scanf("%d%lld",&n,&k);
    for (reg int i = 1;i <= n; ++ i){
        int a;
        scanf("%d",&a);
        sum += 1ll * a;
         ++ R[sum % k];
    }
    ans += R[0];
    for (reg int i = 0;i <=k - 1; ++ i){
        if (R[i] <= 1)
            continue;
        if (R[i] == 2)
            ans = (ans + 1) % mod;
        else ans = (ans + C(R[i],2)) % mod;
    }
    printf("%lld\n",ans);
    return 0;
}
 

猜你喜欢

转载自blog.csdn.net/qq_43904786/article/details/87556229