连续自然数之和
目录
目录
题目描述
有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;
}